redis 7.0.0b3__tar.gz → 7.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. {redis-7.0.0b3 → redis-7.0.1}/PKG-INFO +15 -4
  2. {redis-7.0.0b3 → redis-7.0.1}/README.md +14 -3
  3. redis-7.0.1/dev_requirements.txt +30 -0
  4. {redis-7.0.0b3 → redis-7.0.1}/redis/__init__.py +1 -1
  5. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/base.py +6 -0
  6. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/helpers.py +64 -6
  7. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/connection.py +1 -1
  8. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/client.py +2 -0
  9. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/config.py +2 -2
  10. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/healthcheck.py +4 -11
  11. {redis-7.0.0b3 → redis-7.0.1}/redis/client.py +27 -24
  12. {redis-7.0.0b3 → redis-7.0.1}/redis/cluster.py +6 -0
  13. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/core.py +54 -26
  14. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/json/commands.py +2 -2
  15. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/__init__.py +2 -2
  16. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/aggregation.py +24 -26
  17. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/commands.py +10 -10
  18. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/query.py +12 -12
  19. {redis-7.0.0b3 → redis-7.0.1}/redis/connection.py +1613 -1263
  20. {redis-7.0.0b3 → redis-7.0.1}/redis/exceptions.py +8 -0
  21. {redis-7.0.0b3 → redis-7.0.1}/redis/maint_notifications.py +18 -7
  22. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/client.py +2 -0
  23. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/config.py +2 -2
  24. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/healthcheck.py +4 -11
  25. {redis-7.0.0b3 → redis-7.0.1}/redis/utils.py +20 -0
  26. {redis-7.0.0b3 → redis-7.0.1}/tests/conftest.py +9 -0
  27. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_commands.py +37 -7
  28. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_connection_pool.py +28 -5
  29. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_config.py +2 -2
  30. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_healthcheck.py +8 -8
  31. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_search.py +17 -0
  32. {redis-7.0.0b3 → redis-7.0.1}/tests/test_commands.py +57 -13
  33. {redis-7.0.0b3 → redis-7.0.1}/tests/test_connection_pool.py +37 -1
  34. {redis-7.0.0b3 → redis-7.0.1}/tests/test_credentials.py +6 -1
  35. {redis-7.0.0b3 → redis-7.0.1}/tests/test_maint_notifications.py +5 -2
  36. {redis-7.0.0b3 → redis-7.0.1}/tests/test_maint_notifications_handling.py +111 -98
  37. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_config.py +2 -2
  38. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_healthcheck.py +8 -8
  39. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multiprocessing.py +8 -0
  40. redis-7.0.1/tests/test_parsers/test_errors.py +167 -0
  41. {redis-7.0.0b3 → redis-7.0.1}/tests/test_parsers/test_helpers.py +21 -1
  42. {redis-7.0.0b3 → redis-7.0.1}/tests/test_pubsub.py +10 -2
  43. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/conftest.py +0 -4
  44. {redis-7.0.0b3 → redis-7.0.1}/tests/test_search.py +15 -0
  45. redis-7.0.0b3/dev_requirements.txt +0 -17
  46. {redis-7.0.0b3 → redis-7.0.1}/.gitignore +0 -0
  47. {redis-7.0.0b3 → redis-7.0.1}/LICENSE +0 -0
  48. {redis-7.0.0b3 → redis-7.0.1}/pyproject.toml +0 -0
  49. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/__init__.py +0 -0
  50. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/commands.py +0 -0
  51. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/encoders.py +0 -0
  52. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/hiredis.py +0 -0
  53. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/resp2.py +0 -0
  54. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/resp3.py +0 -0
  55. {redis-7.0.0b3 → redis-7.0.1}/redis/_parsers/socket.py +0 -0
  56. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/__init__.py +0 -0
  57. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/client.py +0 -0
  58. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/cluster.py +0 -0
  59. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/http/__init__.py +0 -0
  60. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/http/http_client.py +0 -0
  61. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/lock.py +0 -0
  62. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/__init__.py +0 -0
  63. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/command_executor.py +0 -0
  64. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/database.py +0 -0
  65. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/event.py +0 -0
  66. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/failover.py +0 -0
  67. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/multidb/failure_detector.py +0 -0
  68. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/retry.py +0 -0
  69. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/sentinel.py +0 -0
  70. {redis-7.0.0b3 → redis-7.0.1}/redis/asyncio/utils.py +0 -0
  71. {redis-7.0.0b3 → redis-7.0.1}/redis/auth/__init__.py +0 -0
  72. {redis-7.0.0b3 → redis-7.0.1}/redis/auth/err.py +0 -0
  73. {redis-7.0.0b3 → redis-7.0.1}/redis/auth/idp.py +0 -0
  74. {redis-7.0.0b3 → redis-7.0.1}/redis/auth/token.py +0 -0
  75. {redis-7.0.0b3 → redis-7.0.1}/redis/auth/token_manager.py +0 -0
  76. {redis-7.0.0b3 → redis-7.0.1}/redis/background.py +0 -0
  77. {redis-7.0.0b3 → redis-7.0.1}/redis/backoff.py +0 -0
  78. {redis-7.0.0b3 → redis-7.0.1}/redis/cache.py +0 -0
  79. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/__init__.py +0 -0
  80. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/bf/__init__.py +0 -0
  81. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/bf/commands.py +0 -0
  82. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/bf/info.py +0 -0
  83. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/cluster.py +0 -0
  84. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/helpers.py +0 -0
  85. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/json/__init__.py +0 -0
  86. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/json/_util.py +0 -0
  87. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/json/decoders.py +0 -0
  88. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/json/path.py +0 -0
  89. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/redismodules.py +0 -0
  90. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/_util.py +0 -0
  91. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/dialect.py +0 -0
  92. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/document.py +0 -0
  93. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/field.py +2 -2
  94. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/index_definition.py +0 -0
  95. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/profile_information.py +0 -0
  96. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/querystring.py +0 -0
  97. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/reducers.py +0 -0
  98. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/result.py +0 -0
  99. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/search/suggestion.py +0 -0
  100. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/sentinel.py +0 -0
  101. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/timeseries/__init__.py +0 -0
  102. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/timeseries/commands.py +0 -0
  103. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/timeseries/info.py +0 -0
  104. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/timeseries/utils.py +0 -0
  105. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/vectorset/__init__.py +0 -0
  106. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/vectorset/commands.py +0 -0
  107. {redis-7.0.0b3 → redis-7.0.1}/redis/commands/vectorset/utils.py +0 -0
  108. {redis-7.0.0b3 → redis-7.0.1}/redis/crc.py +0 -0
  109. {redis-7.0.0b3 → redis-7.0.1}/redis/credentials.py +0 -0
  110. {redis-7.0.0b3 → redis-7.0.1}/redis/data_structure.py +0 -0
  111. {redis-7.0.0b3 → redis-7.0.1}/redis/event.py +0 -0
  112. {redis-7.0.0b3 → redis-7.0.1}/redis/http/__init__.py +0 -0
  113. {redis-7.0.0b3 → redis-7.0.1}/redis/http/http_client.py +0 -0
  114. {redis-7.0.0b3 → redis-7.0.1}/redis/lock.py +0 -0
  115. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/__init__.py +0 -0
  116. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/circuit.py +0 -0
  117. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/command_executor.py +0 -0
  118. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/database.py +0 -0
  119. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/event.py +0 -0
  120. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/exception.py +0 -0
  121. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/failover.py +0 -0
  122. {redis-7.0.0b3 → redis-7.0.1}/redis/multidb/failure_detector.py +0 -0
  123. {redis-7.0.0b3 → redis-7.0.1}/redis/ocsp.py +0 -0
  124. {redis-7.0.0b3 → redis-7.0.1}/redis/py.typed +0 -0
  125. {redis-7.0.0b3 → redis-7.0.1}/redis/retry.py +0 -0
  126. {redis-7.0.0b3 → redis-7.0.1}/redis/sentinel.py +0 -0
  127. {redis-7.0.0b3 → redis-7.0.1}/redis/typing.py +0 -0
  128. {redis-7.0.0b3 → redis-7.0.1}/tests/__init__.py +0 -0
  129. {redis-7.0.0b3 → redis-7.0.1}/tests/entraid_utils.py +0 -0
  130. {redis-7.0.0b3 → redis-7.0.1}/tests/mocks.py +0 -0
  131. {redis-7.0.0b3 → redis-7.0.1}/tests/ssl_utils.py +0 -0
  132. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/__init__.py +0 -0
  133. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/compat.py +0 -0
  134. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/conftest.py +0 -0
  135. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/mocks.py +0 -0
  136. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_bloom.py +0 -0
  137. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_cluster.py +0 -0
  138. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_cluster_transaction.py +0 -0
  139. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_connect.py +0 -0
  140. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_connection.py +0 -0
  141. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_credentials.py +0 -0
  142. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_cwe_404.py +0 -0
  143. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_encoding.py +0 -0
  144. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_hash.py +0 -0
  145. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_json.py +0 -0
  146. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_lock.py +0 -0
  147. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_monitor.py +0 -0
  148. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/__init__.py +0 -0
  149. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/conftest.py +0 -0
  150. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_client.py +0 -0
  151. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_command_executor.py +0 -0
  152. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_failover.py +0 -0
  153. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_failure_detector.py +0 -0
  154. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_multidb/test_pipeline.py +0 -0
  155. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_pipeline.py +0 -0
  156. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_pubsub.py +0 -0
  157. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_retry.py +0 -0
  158. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_scenario/__init__.py +0 -0
  159. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_scenario/conftest.py +0 -0
  160. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_scenario/test_active_active.py +0 -0
  161. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_scripting.py +0 -0
  162. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_sentinel.py +0 -0
  163. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
  164. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_ssl.py +0 -0
  165. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_timeseries.py +0 -0
  166. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_usage_counter.py +0 -0
  167. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_utils.py +0 -0
  168. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/test_vsets.py +0 -0
  169. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
  170. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/testdata/titles.csv +0 -0
  171. {redis-7.0.0b3 → redis-7.0.1}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
  172. {redis-7.0.0b3 → redis-7.0.1}/tests/test_auth/__init__.py +0 -0
  173. {redis-7.0.0b3 → redis-7.0.1}/tests/test_auth/test_token.py +0 -0
  174. {redis-7.0.0b3 → redis-7.0.1}/tests/test_auth/test_token_manager.py +0 -0
  175. {redis-7.0.0b3 → redis-7.0.1}/tests/test_background.py +0 -0
  176. {redis-7.0.0b3 → redis-7.0.1}/tests/test_backoff.py +0 -0
  177. {redis-7.0.0b3 → redis-7.0.1}/tests/test_bloom.py +0 -0
  178. {redis-7.0.0b3 → redis-7.0.1}/tests/test_cache.py +0 -0
  179. {redis-7.0.0b3 → redis-7.0.1}/tests/test_cluster.py +0 -0
  180. {redis-7.0.0b3 → redis-7.0.1}/tests/test_cluster_transaction.py +0 -0
  181. {redis-7.0.0b3 → redis-7.0.1}/tests/test_command_parser.py +0 -0
  182. {redis-7.0.0b3 → redis-7.0.1}/tests/test_connect.py +0 -0
  183. {redis-7.0.0b3 → redis-7.0.1}/tests/test_connection.py +0 -0
  184. {redis-7.0.0b3 → redis-7.0.1}/tests/test_data_structure.py +0 -0
  185. {redis-7.0.0b3 → redis-7.0.1}/tests/test_encoding.py +0 -0
  186. {redis-7.0.0b3 → redis-7.0.1}/tests/test_event.py +0 -0
  187. {redis-7.0.0b3 → redis-7.0.1}/tests/test_function.py +0 -0
  188. {redis-7.0.0b3 → redis-7.0.1}/tests/test_hash.py +0 -0
  189. {redis-7.0.0b3 → redis-7.0.1}/tests/test_helpers.py +0 -0
  190. {redis-7.0.0b3 → redis-7.0.1}/tests/test_http/__init__.py +0 -0
  191. {redis-7.0.0b3 → redis-7.0.1}/tests/test_http/test_http_client.py +0 -0
  192. {redis-7.0.0b3 → redis-7.0.1}/tests/test_json.py +0 -0
  193. {redis-7.0.0b3 → redis-7.0.1}/tests/test_lock.py +0 -0
  194. {redis-7.0.0b3 → redis-7.0.1}/tests/test_max_connections_error.py +0 -0
  195. {redis-7.0.0b3 → redis-7.0.1}/tests/test_monitor.py +0 -0
  196. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/__init__.py +0 -0
  197. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/conftest.py +0 -0
  198. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_circuit.py +0 -0
  199. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_client.py +0 -0
  200. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_command_executor.py +0 -0
  201. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_failover.py +0 -0
  202. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_failure_detector.py +0 -0
  203. {redis-7.0.0b3 → redis-7.0.1}/tests/test_multidb/test_pipeline.py +0 -0
  204. {redis-7.0.0b3 → redis-7.0.1}/tests/test_pipeline.py +0 -0
  205. {redis-7.0.0b3 → redis-7.0.1}/tests/test_retry.py +0 -0
  206. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/__init__.py +0 -0
  207. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/fault_injector_client.py +0 -0
  208. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/maint_notifications_helpers.py +0 -0
  209. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/test_active_active.py +0 -0
  210. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scenario/test_maint_notifications.py +0 -0
  211. {redis-7.0.0b3 → redis-7.0.1}/tests/test_scripting.py +0 -0
  212. {redis-7.0.0b3 → redis-7.0.1}/tests/test_sentinel.py +0 -0
  213. {redis-7.0.0b3 → redis-7.0.1}/tests/test_sentinel_managed_connection.py +0 -0
  214. {redis-7.0.0b3 → redis-7.0.1}/tests/test_ssl.py +0 -0
  215. {redis-7.0.0b3 → redis-7.0.1}/tests/test_timeseries.py +0 -0
  216. {redis-7.0.0b3 → redis-7.0.1}/tests/test_utils.py +0 -0
  217. {redis-7.0.0b3 → redis-7.0.1}/tests/test_vsets.py +0 -0
  218. {redis-7.0.0b3 → redis-7.0.1}/tests/testdata/jsontestdata.py +0 -0
  219. {redis-7.0.0b3 → redis-7.0.1}/tests/testdata/titles.csv +0 -0
  220. {redis-7.0.0b3 → redis-7.0.1}/tests/testdata/will_play_text.csv.bz2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redis
3
- Version: 7.0.0b3
3
+ Version: 7.0.1
4
4
  Summary: Python client for Redis database and key-value store
5
5
  Project-URL: Changes, https://github.com/redis/redis-py/releases
6
6
  Project-URL: Code, https://github.com/redis/redis-py
@@ -55,8 +55,9 @@ The Python interface to the Redis key-value store.
55
55
 
56
56
  ---------------------------------------------
57
57
 
58
- **Note:** redis-py 5.0 will be the last version of redis-py to support Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 will support Python 3.8+.
59
- **Note:** redis-py 6.1.0 will be the last version of redis-py to support Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 will support Python 3.9+.
58
+ **Note:** redis-py 5.0 is the last version of redis-py that supports Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 supports Python 3.8+.<br>
59
+ **Note:** redis-py 6.1.0 is the last version of redis-py that supports Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 supports Python 3.9+.
60
+
60
61
  ---------------------------------------------
61
62
 
62
63
  ## How do I Redis?
@@ -101,7 +102,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
101
102
 
102
103
  ## Supported Redis Versions
103
104
 
104
- The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES) and [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES).
105
+ The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) and [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES).
105
106
 
106
107
  The table below highlights version compatibility of the most-recent library versions and redis versions.
107
108
 
@@ -235,6 +236,16 @@ By default, the client now overrides the server-side dialect with version 2, aut
235
236
 
236
237
  You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).
237
238
 
239
+ ### Multi-database client (Active-Active)
240
+
241
+ The multi-database client allows your application to connect to multiple Redis databases, which are typically replicas of each other. It is designed to work with Redis Software and Redis Cloud Active-Active setups. The client continuously monitors database health, detects failures, and automatically fails over to the next healthy database using a configurable strategy. When the original database becomes healthy again, the client can automatically switch back to it.<br>
242
+ This is useful when:
243
+
244
+ 1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://redis.io/docs/latest/operate/rs/databases/active-active/) clusters.
245
+ 2. You want your application to connect to one deployment at a time and to fail over to the next available deployment if the first deployment becomes unavailable.
246
+
247
+ For the complete failover configuration options and examples, see the [Multi-database client docs](https://redis.readthedocs.io/en/latest/multi_database.html).
248
+
238
249
  ---------------------------------------------
239
250
 
240
251
  ### Author
@@ -13,8 +13,9 @@ The Python interface to the Redis key-value store.
13
13
 
14
14
  ---------------------------------------------
15
15
 
16
- **Note:** redis-py 5.0 will be the last version of redis-py to support Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 will support Python 3.8+.
17
- **Note:** redis-py 6.1.0 will be the last version of redis-py to support Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 will support Python 3.9+.
16
+ **Note:** redis-py 5.0 is the last version of redis-py that supports Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 supports Python 3.8+.<br>
17
+ **Note:** redis-py 6.1.0 is the last version of redis-py that supports Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 supports Python 3.9+.
18
+
18
19
  ---------------------------------------------
19
20
 
20
21
  ## How do I Redis?
@@ -59,7 +60,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
59
60
 
60
61
  ## Supported Redis Versions
61
62
 
62
- The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES) and [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES).
63
+ The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) and [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES).
63
64
 
64
65
  The table below highlights version compatibility of the most-recent library versions and redis versions.
65
66
 
@@ -193,6 +194,16 @@ By default, the client now overrides the server-side dialect with version 2, aut
193
194
 
194
195
  You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).
195
196
 
197
+ ### Multi-database client (Active-Active)
198
+
199
+ The multi-database client allows your application to connect to multiple Redis databases, which are typically replicas of each other. It is designed to work with Redis Software and Redis Cloud Active-Active setups. The client continuously monitors database health, detects failures, and automatically fails over to the next healthy database using a configurable strategy. When the original database becomes healthy again, the client can automatically switch back to it.<br>
200
+ This is useful when:
201
+
202
+ 1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://redis.io/docs/latest/operate/rs/databases/active-active/) clusters.
203
+ 2. You want your application to connect to one deployment at a time and to fail over to the next available deployment if the first deployment becomes unavailable.
204
+
205
+ For the complete failover configuration options and examples, see the [Multi-database client docs](https://redis.readthedocs.io/en/latest/multi_database.html).
206
+
196
207
  ---------------------------------------------
197
208
 
198
209
  ### Author
@@ -0,0 +1,30 @@
1
+ build
2
+ build==1.2.2.post1 ; platform_python_implementation == "PyPy" or python_version < "3.10"
3
+ click==8.0.4
4
+ invoke==2.2.0
5
+ mock
6
+ mock==5.1.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
7
+ packaging>=20.4
8
+ packaging==24.2 ; platform_python_implementation == "PyPy" or python_version < "3.10"
9
+
10
+ pytest
11
+ pytest==8.3.4 ; platform_python_implementation == "PyPy" or python_version < "3.10"
12
+ pytest-asyncio>=0.23.0
13
+ pytest-asyncio==1.1.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
14
+ pytest-cov
15
+ pytest-cov==6.0.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
16
+ coverage==7.6.12 ; platform_python_implementation == "PyPy" or python_version < "3.10"
17
+ pytest-profiling==1.8.1
18
+ pytest-timeout
19
+ pytest-timeout==2.3.1 ; platform_python_implementation == "PyPy" or python_version < "3.10"
20
+
21
+ ruff==0.9.6
22
+ ujson>=4.2.0
23
+ uvloop<=0.21.0; platform_python_implementation == "CPython"
24
+ vulture>=2.3.0
25
+
26
+ numpy>=1.24.0 ; platform_python_implementation == "CPython"
27
+ numpy>=1.24.0,<2.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
28
+
29
+ redis-entraid==1.0.0
30
+ pybreaker>=1.4.0
@@ -46,7 +46,7 @@ def int_or_str(value):
46
46
  return value
47
47
 
48
48
 
49
- __version__ = "7.0.0b3"
49
+ __version__ = "7.0.1"
50
50
  VERSION = tuple(map(int_or_str, __version__.split(".")))
51
51
 
52
52
 
@@ -27,6 +27,7 @@ from ..exceptions import (
27
27
  ClusterDownError,
28
28
  ConnectionError,
29
29
  ExecAbortError,
30
+ ExternalAuthProviderError,
30
31
  MasterDownError,
31
32
  ModuleError,
32
33
  MovedError,
@@ -60,6 +61,10 @@ NO_AUTH_SET_ERROR = {
60
61
  "Client sent AUTH, but no password is set": AuthenticationError,
61
62
  }
62
63
 
64
+ EXTERNAL_AUTH_PROVIDER_ERROR = {
65
+ "problem with LDAP service": ExternalAuthProviderError,
66
+ }
67
+
63
68
  logger = logging.getLogger(__name__)
64
69
 
65
70
 
@@ -81,6 +86,7 @@ class BaseParser(ABC):
81
86
  NO_SUCH_MODULE_ERROR: ModuleError,
82
87
  MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError,
83
88
  **NO_AUTH_SET_ERROR,
89
+ **EXTERNAL_AUTH_PROVIDER_ERROR,
84
90
  },
85
91
  "OOM": OutOfMemoryError,
86
92
  "WRONGPASS": AuthenticationError,
@@ -224,6 +224,39 @@ def zset_score_pairs(response, **options):
224
224
  return list(zip(it, map(score_cast_func, it)))
225
225
 
226
226
 
227
+ def zset_score_for_rank(response, **options):
228
+ """
229
+ If ``withscores`` is specified in the options, return the response as
230
+ a [value, score] pair
231
+ """
232
+ if not response or not options.get("withscore"):
233
+ return response
234
+ score_cast_func = options.get("score_cast_func", float)
235
+ return [response[0], score_cast_func(response[1])]
236
+
237
+
238
+ def zset_score_pairs_resp3(response, **options):
239
+ """
240
+ If ``withscores`` is specified in the options, return the response as
241
+ a list of [value, score] pairs
242
+ """
243
+ if not response or not options.get("withscores"):
244
+ return response
245
+ score_cast_func = options.get("score_cast_func", float)
246
+ return [[name, score_cast_func(val)] for name, val in response]
247
+
248
+
249
+ def zset_score_for_rank_resp3(response, **options):
250
+ """
251
+ If ``withscores`` is specified in the options, return the response as
252
+ a [value, score] pair
253
+ """
254
+ if not response or not options.get("withscore"):
255
+ return response
256
+ score_cast_func = options.get("score_cast_func", float)
257
+ return [response[0], score_cast_func(response[1])]
258
+
259
+
227
260
  def sort_return_tuples(response, **options):
228
261
  """
229
262
  If ``groups`` is specified, return the response as a list of
@@ -349,8 +382,22 @@ def parse_zadd(response, **options):
349
382
  def parse_client_list(response, **options):
350
383
  clients = []
351
384
  for c in str_if_bytes(response).splitlines():
352
- # Values might contain '='
353
- clients.append(dict(pair.split("=", 1) for pair in c.split(" ")))
385
+ client_dict = {}
386
+ tokens = c.split(" ")
387
+ last_key = None
388
+ for token in tokens:
389
+ if "=" in token:
390
+ # Values might contain '='
391
+ key, value = token.split("=", 1)
392
+ client_dict[key] = value
393
+ last_key = key
394
+ else:
395
+ # Values may include spaces. For instance, when running Redis via a Unix socket — such as
396
+ # "/tmp/redis sock/redis.sock" — the addr or laddr field will include a space.
397
+ client_dict[last_key] += " " + token
398
+
399
+ if client_dict:
400
+ clients.append(client_dict)
354
401
  return clients
355
402
 
356
403
 
@@ -797,10 +844,14 @@ _RedisCallbacksRESP2 = {
797
844
  "SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
798
845
  ),
799
846
  **string_keys_to_dict(
800
- "ZDIFF ZINTER ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZRANK ZREVRANGE "
801
- "ZREVRANGEBYSCORE ZREVRANK ZUNION",
847
+ "ZDIFF ZINTER ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZREVRANGE "
848
+ "ZREVRANGEBYSCORE ZUNION",
802
849
  zset_score_pairs,
803
850
  ),
851
+ **string_keys_to_dict(
852
+ "ZREVRANK ZRANK",
853
+ zset_score_for_rank,
854
+ ),
804
855
  **string_keys_to_dict("ZINCRBY ZSCORE", float_or_none),
805
856
  **string_keys_to_dict("BGREWRITEAOF BGSAVE", lambda r: True),
806
857
  **string_keys_to_dict("BLPOP BRPOP", lambda r: r and tuple(r) or None),
@@ -844,10 +895,17 @@ _RedisCallbacksRESP3 = {
844
895
  "SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
845
896
  ),
846
897
  **string_keys_to_dict(
847
- "ZRANGE ZINTER ZPOPMAX ZPOPMIN ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE "
848
- "ZUNION HGETALL XREADGROUP",
898
+ "ZRANGE ZINTER ZPOPMAX ZPOPMIN HGETALL XREADGROUP",
849
899
  lambda r, **kwargs: r,
850
900
  ),
901
+ **string_keys_to_dict(
902
+ "ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
903
+ zset_score_pairs_resp3,
904
+ ),
905
+ **string_keys_to_dict(
906
+ "ZREVRANK ZRANK",
907
+ zset_score_for_rank_resp3,
908
+ ),
851
909
  **string_keys_to_dict("XREAD XREADGROUP", parse_xread_resp3),
852
910
  "ACL LOG": lambda r: (
853
911
  [
@@ -1356,7 +1356,7 @@ class BlockingConnectionPool(ConnectionPool):
1356
1356
  def __init__(
1357
1357
  self,
1358
1358
  max_connections: int = 50,
1359
- timeout: Optional[int] = 20,
1359
+ timeout: Optional[float] = 20,
1360
1360
  connection_class: Type[AbstractConnection] = Connection,
1361
1361
  queue_class: Type[asyncio.Queue] = asyncio.LifoQueue, # deprecated
1362
1362
  **connection_kwargs,
@@ -14,10 +14,12 @@ from redis.multidb.circuit import CircuitBreaker
14
14
  from redis.multidb.circuit import State as CBState
15
15
  from redis.multidb.exception import NoValidDatabaseException, UnhealthyDatabaseException
16
16
  from redis.typing import ChannelT, EncodableT, KeyT
17
+ from redis.utils import experimental
17
18
 
18
19
  logger = logging.getLogger(__name__)
19
20
 
20
21
 
22
+ @experimental
21
23
  class MultiDBClient(AsyncRedisModuleCommands, AsyncCoreCommands):
22
24
  """
23
25
  Client that operates on multiple logical Redis databases.
@@ -20,9 +20,9 @@ from redis.asyncio.multidb.healthcheck import (
20
20
  DEFAULT_HEALTH_CHECK_INTERVAL,
21
21
  DEFAULT_HEALTH_CHECK_POLICY,
22
22
  DEFAULT_HEALTH_CHECK_PROBES,
23
- EchoHealthCheck,
24
23
  HealthCheck,
25
24
  HealthCheckPolicies,
25
+ PingHealthCheck,
26
26
  )
27
27
  from redis.asyncio.retry import Retry
28
28
  from redis.backoff import ExponentialWithJitterBackoff, NoBackoff
@@ -203,7 +203,7 @@ class MultiDbConfig:
203
203
 
204
204
  def default_health_checks(self) -> List[HealthCheck]:
205
205
  return [
206
- EchoHealthCheck(),
206
+ PingHealthCheck(),
207
207
  ]
208
208
 
209
209
  def default_failover_strategy(self) -> AsyncFailoverStrategy:
@@ -170,26 +170,19 @@ class HealthCheckPolicies(Enum):
170
170
  DEFAULT_HEALTH_CHECK_POLICY: HealthCheckPolicies = HealthCheckPolicies.HEALTHY_ALL
171
171
 
172
172
 
173
- class EchoHealthCheck(HealthCheck):
173
+ class PingHealthCheck(HealthCheck):
174
174
  """
175
- Health check based on ECHO command.
175
+ Health check based on PING command.
176
176
  """
177
177
 
178
178
  async def check_health(self, database) -> bool:
179
- expected_message = ["healthcheck", b"healthcheck"]
180
-
181
179
  if isinstance(database.client, Redis):
182
- actual_message = await database.client.execute_command(
183
- "ECHO", "healthcheck"
184
- )
185
- return actual_message in expected_message
180
+ return await database.client.execute_command("PING")
186
181
  else:
187
182
  # For a cluster checks if all nodes are healthy.
188
183
  all_nodes = database.client.get_nodes()
189
184
  for node in all_nodes:
190
- actual_message = await node.execute_command("ECHO", "healthcheck")
191
-
192
- if actual_message not in expected_message:
185
+ if not await node.redis_connection.execute_command("PING"):
193
186
  return False
194
187
 
195
188
  return True
@@ -58,7 +58,6 @@ from redis.exceptions import (
58
58
  from redis.lock import Lock
59
59
  from redis.maint_notifications import (
60
60
  MaintNotificationsConfig,
61
- MaintNotificationsPoolHandler,
62
61
  )
63
62
  from redis.retry import Retry
64
63
  from redis.utils import (
@@ -278,6 +277,17 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
278
277
  single_connection_client:
279
278
  if `True`, connection pool is not used. In that case `Redis`
280
279
  instance use is not thread safe.
280
+ decode_responses:
281
+ if `True`, the response will be decoded to utf-8.
282
+ Argument is ignored when connection_pool is provided.
283
+ maint_notifications_config:
284
+ configuration the pool to support maintenance notifications - see
285
+ `redis.maint_notifications.MaintNotificationsConfig` for details.
286
+ Only supported with RESP3
287
+ If not provided and protocol is RESP3, the maintenance notifications
288
+ will be enabled by default (logic is included in the connection pool
289
+ initialization).
290
+ Argument is ignored when connection_pool is provided.
281
291
  """
282
292
  if event_dispatcher is None:
283
293
  self._event_dispatcher = EventDispatcher()
@@ -354,6 +364,22 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
354
364
  "cache_config": cache_config,
355
365
  }
356
366
  )
367
+ maint_notifications_enabled = (
368
+ maint_notifications_config and maint_notifications_config.enabled
369
+ )
370
+ if maint_notifications_enabled and protocol not in [
371
+ 3,
372
+ "3",
373
+ ]:
374
+ raise RedisError(
375
+ "Maintenance notifications handlers on connection are only supported with RESP version 3"
376
+ )
377
+ if maint_notifications_config:
378
+ kwargs.update(
379
+ {
380
+ "maint_notifications_config": maint_notifications_config,
381
+ }
382
+ )
357
383
  connection_pool = ConnectionPool(**kwargs)
358
384
  self._event_dispatcher.dispatch(
359
385
  AfterPooledConnectionsInstantiationEvent(
@@ -377,23 +403,6 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
377
403
  ]:
378
404
  raise RedisError("Client caching is only supported with RESP version 3")
379
405
 
380
- if maint_notifications_config and self.connection_pool.get_protocol() not in [
381
- 3,
382
- "3",
383
- ]:
384
- raise RedisError(
385
- "Push handlers on connection are only supported with RESP version 3"
386
- )
387
- if maint_notifications_config and maint_notifications_config.enabled:
388
- self.maint_notifications_pool_handler = MaintNotificationsPoolHandler(
389
- self.connection_pool, maint_notifications_config
390
- )
391
- self.connection_pool.set_maint_notifications_pool_handler(
392
- self.maint_notifications_pool_handler
393
- )
394
- else:
395
- self.maint_notifications_pool_handler = None
396
-
397
406
  self.single_connection_lock = threading.RLock()
398
407
  self.connection = None
399
408
  self._single_connection_client = single_connection_client
@@ -591,15 +600,9 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
591
600
  return Monitor(self.connection_pool)
592
601
 
593
602
  def client(self):
594
- maint_notifications_config = (
595
- None
596
- if self.maint_notifications_pool_handler is None
597
- else self.maint_notifications_pool_handler.config
598
- )
599
603
  return self.__class__(
600
604
  connection_pool=self.connection_pool,
601
605
  single_connection_client=True,
602
- maint_notifications_config=maint_notifications_config,
603
606
  )
604
607
 
605
608
  def __enter__(self):
@@ -50,6 +50,7 @@ from redis.exceptions import (
50
50
  WatchError,
51
51
  )
52
52
  from redis.lock import Lock
53
+ from redis.maint_notifications import MaintNotificationsConfig
53
54
  from redis.retry import Retry
54
55
  from redis.utils import (
55
56
  deprecated_args,
@@ -1663,6 +1664,11 @@ class NodesManager:
1663
1664
  backoff=NoBackoff(), retries=0, supported_errors=(ConnectionError,)
1664
1665
  )
1665
1666
 
1667
+ protocol = kwargs.get("protocol", None)
1668
+ if protocol in [3, "3"]:
1669
+ kwargs.update(
1670
+ {"maint_notifications_config": MaintNotificationsConfig(enabled=False)}
1671
+ )
1666
1672
  if self.from_url:
1667
1673
  # Create a redis node with a costumed connection pool
1668
1674
  kwargs.update({"host": host})
@@ -830,7 +830,7 @@ class ManagementCommands(CommandsProtocol):
830
830
 
831
831
  return self.execute_command("COMMAND LIST", *pieces)
832
832
 
833
- def command_getkeysandflags(self, *args: List[str]) -> List[Union[str, List[str]]]:
833
+ def command_getkeysandflags(self, *args: str) -> List[Union[str, List[str]]]:
834
834
  """
835
835
  Returns array of keys from a full Redis command and their usage flags.
836
836
 
@@ -848,7 +848,7 @@ class ManagementCommands(CommandsProtocol):
848
848
  )
849
849
 
850
850
  def config_get(
851
- self, pattern: PatternT = "*", *args: List[PatternT], **kwargs
851
+ self, pattern: PatternT = "*", *args: PatternT, **kwargs
852
852
  ) -> ResponseT:
853
853
  """
854
854
  Return a dictionary of configuration based on the ``pattern``
@@ -861,7 +861,7 @@ class ManagementCommands(CommandsProtocol):
861
861
  self,
862
862
  name: KeyT,
863
863
  value: EncodableT,
864
- *args: List[Union[KeyT, EncodableT]],
864
+ *args: Union[KeyT, EncodableT],
865
865
  **kwargs,
866
866
  ) -> ResponseT:
867
867
  """Set config item ``name`` with ``value``
@@ -987,9 +987,7 @@ class ManagementCommands(CommandsProtocol):
987
987
  """
988
988
  return self.execute_command("SELECT", index, **kwargs)
989
989
 
990
- def info(
991
- self, section: Optional[str] = None, *args: List[str], **kwargs
992
- ) -> ResponseT:
990
+ def info(self, section: Optional[str] = None, *args: str, **kwargs) -> ResponseT:
993
991
  """
994
992
  Returns a dictionary containing information about the Redis server
995
993
 
@@ -2606,7 +2604,7 @@ class ListCommands(CommandsProtocol):
2606
2604
  self,
2607
2605
  timeout: float,
2608
2606
  numkeys: int,
2609
- *args: List[str],
2607
+ *args: str,
2610
2608
  direction: str,
2611
2609
  count: Optional[int] = 1,
2612
2610
  ) -> Optional[list]:
@@ -2619,14 +2617,14 @@ class ListCommands(CommandsProtocol):
2619
2617
 
2620
2618
  For more information, see https://redis.io/commands/blmpop
2621
2619
  """
2622
- args = [timeout, numkeys, *args, direction, "COUNT", count]
2620
+ cmd_args = [timeout, numkeys, *args, direction, "COUNT", count]
2623
2621
 
2624
- return self.execute_command("BLMPOP", *args)
2622
+ return self.execute_command("BLMPOP", *cmd_args)
2625
2623
 
2626
2624
  def lmpop(
2627
2625
  self,
2628
2626
  num_keys: int,
2629
- *args: List[str],
2627
+ *args: str,
2630
2628
  direction: str,
2631
2629
  count: Optional[int] = 1,
2632
2630
  ) -> Union[Awaitable[list], list]:
@@ -2636,11 +2634,11 @@ class ListCommands(CommandsProtocol):
2636
2634
 
2637
2635
  For more information, see https://redis.io/commands/lmpop
2638
2636
  """
2639
- args = [num_keys] + list(args) + [direction]
2637
+ cmd_args = [num_keys] + list(args) + [direction]
2640
2638
  if count != 1:
2641
- args.extend(["COUNT", count])
2639
+ cmd_args.extend(["COUNT", count])
2642
2640
 
2643
- return self.execute_command("LMPOP", *args)
2641
+ return self.execute_command("LMPOP", *cmd_args)
2644
2642
 
2645
2643
  def lindex(
2646
2644
  self, name: str, index: int
@@ -4778,6 +4776,7 @@ class SortedSetCommands(CommandsProtocol):
4778
4776
  name: KeyT,
4779
4777
  value: EncodableT,
4780
4778
  withscore: bool = False,
4779
+ score_cast_func: Union[type, Callable] = float,
4781
4780
  ) -> ResponseT:
4782
4781
  """
4783
4782
  Returns a 0-based value indicating the rank of ``value`` in sorted set
@@ -4785,11 +4784,17 @@ class SortedSetCommands(CommandsProtocol):
4785
4784
  The optional WITHSCORE argument supplements the command's
4786
4785
  reply with the score of the element returned.
4787
4786
 
4787
+ ``score_cast_func`` a callable used to cast the score return value
4788
+
4788
4789
  For more information, see https://redis.io/commands/zrank
4789
4790
  """
4791
+ pieces = ["ZRANK", name, value]
4790
4792
  if withscore:
4791
- return self.execute_command("ZRANK", name, value, "WITHSCORE", keys=[name])
4792
- return self.execute_command("ZRANK", name, value, keys=[name])
4793
+ pieces.append("WITHSCORE")
4794
+
4795
+ options = {"withscore": withscore, "score_cast_func": score_cast_func}
4796
+
4797
+ return self.execute_command(*pieces, **options)
4793
4798
 
4794
4799
  def zrem(self, name: KeyT, *values: FieldT) -> ResponseT:
4795
4800
  """
@@ -4837,6 +4842,7 @@ class SortedSetCommands(CommandsProtocol):
4837
4842
  name: KeyT,
4838
4843
  value: EncodableT,
4839
4844
  withscore: bool = False,
4845
+ score_cast_func: Union[type, Callable] = float,
4840
4846
  ) -> ResponseT:
4841
4847
  """
4842
4848
  Returns a 0-based value indicating the descending rank of
@@ -4844,13 +4850,17 @@ class SortedSetCommands(CommandsProtocol):
4844
4850
  The optional ``withscore`` argument supplements the command's
4845
4851
  reply with the score of the element returned.
4846
4852
 
4853
+ ``score_cast_func`` a callable used to cast the score return value
4854
+
4847
4855
  For more information, see https://redis.io/commands/zrevrank
4848
4856
  """
4857
+ pieces = ["ZREVRANK", name, value]
4849
4858
  if withscore:
4850
- return self.execute_command(
4851
- "ZREVRANK", name, value, "WITHSCORE", keys=[name]
4852
- )
4853
- return self.execute_command("ZREVRANK", name, value, keys=[name])
4859
+ pieces.append("WITHSCORE")
4860
+
4861
+ options = {"withscore": withscore, "score_cast_func": score_cast_func}
4862
+
4863
+ return self.execute_command(*pieces, **options)
4854
4864
 
4855
4865
  def zscore(self, name: KeyT, value: EncodableT) -> ResponseT:
4856
4866
  """
@@ -4865,6 +4875,7 @@ class SortedSetCommands(CommandsProtocol):
4865
4875
  keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4866
4876
  aggregate: Optional[str] = None,
4867
4877
  withscores: bool = False,
4878
+ score_cast_func: Union[type, Callable] = float,
4868
4879
  ) -> ResponseT:
4869
4880
  """
4870
4881
  Return the union of multiple sorted sets specified by ``keys``.
@@ -4872,9 +4883,18 @@ class SortedSetCommands(CommandsProtocol):
4872
4883
  Scores will be aggregated based on the ``aggregate``, or SUM if
4873
4884
  none is provided.
4874
4885
 
4886
+ ``score_cast_func`` a callable used to cast the score return value
4887
+
4875
4888
  For more information, see https://redis.io/commands/zunion
4876
4889
  """
4877
- return self._zaggregate("ZUNION", None, keys, aggregate, withscores=withscores)
4890
+ return self._zaggregate(
4891
+ "ZUNION",
4892
+ None,
4893
+ keys,
4894
+ aggregate,
4895
+ withscores=withscores,
4896
+ score_cast_func=score_cast_func,
4897
+ )
4878
4898
 
4879
4899
  def zunionstore(
4880
4900
  self,
@@ -5863,12 +5883,16 @@ class ScriptCommands(CommandsProtocol):
5863
5883
  """
5864
5884
 
5865
5885
  def _eval(
5866
- self, command: str, script: str, numkeys: int, *keys_and_args: str
5886
+ self,
5887
+ command: str,
5888
+ script: str,
5889
+ numkeys: int,
5890
+ *keys_and_args: Union[KeyT, EncodableT],
5867
5891
  ) -> Union[Awaitable[str], str]:
5868
5892
  return self.execute_command(command, script, numkeys, *keys_and_args)
5869
5893
 
5870
5894
  def eval(
5871
- self, script: str, numkeys: int, *keys_and_args: str
5895
+ self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5872
5896
  ) -> Union[Awaitable[str], str]:
5873
5897
  """
5874
5898
  Execute the Lua ``script``, specifying the ``numkeys`` the script
@@ -5883,7 +5907,7 @@ class ScriptCommands(CommandsProtocol):
5883
5907
  return self._eval("EVAL", script, numkeys, *keys_and_args)
5884
5908
 
5885
5909
  def eval_ro(
5886
- self, script: str, numkeys: int, *keys_and_args: str
5910
+ self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5887
5911
  ) -> Union[Awaitable[str], str]:
5888
5912
  """
5889
5913
  The read-only variant of the EVAL command
@@ -5897,12 +5921,16 @@ class ScriptCommands(CommandsProtocol):
5897
5921
  return self._eval("EVAL_RO", script, numkeys, *keys_and_args)
5898
5922
 
5899
5923
  def _evalsha(
5900
- self, command: str, sha: str, numkeys: int, *keys_and_args: list
5924
+ self,
5925
+ command: str,
5926
+ sha: str,
5927
+ numkeys: int,
5928
+ *keys_and_args: Union[KeyT, EncodableT],
5901
5929
  ) -> Union[Awaitable[str], str]:
5902
5930
  return self.execute_command(command, sha, numkeys, *keys_and_args)
5903
5931
 
5904
5932
  def evalsha(
5905
- self, sha: str, numkeys: int, *keys_and_args: str
5933
+ self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5906
5934
  ) -> Union[Awaitable[str], str]:
5907
5935
  """
5908
5936
  Use the ``sha`` to execute a Lua script already registered via EVAL
@@ -5918,7 +5946,7 @@ class ScriptCommands(CommandsProtocol):
5918
5946
  return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
5919
5947
 
5920
5948
  def evalsha_ro(
5921
- self, sha: str, numkeys: int, *keys_and_args: str
5949
+ self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5922
5950
  ) -> Union[Awaitable[str], str]:
5923
5951
  """
5924
5952
  The read-only variant of the EVALSHA command