redis 7.0.0b1__tar.gz → 7.0.0b3__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 (221) hide show
  1. {redis-7.0.0b1 → redis-7.0.0b3}/PKG-INFO +3 -1
  2. {redis-7.0.0b1 → redis-7.0.0b3}/dev_requirements.txt +1 -0
  3. {redis-7.0.0b1 → redis-7.0.0b3}/pyproject.toml +9 -0
  4. {redis-7.0.0b1 → redis-7.0.0b3}/redis/__init__.py +1 -1
  5. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/base.py +36 -22
  6. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/client.py +20 -6
  7. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/cluster.py +11 -2
  8. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/connection.py +61 -1
  9. redis-7.0.0b3/redis/asyncio/http/http_client.py +265 -0
  10. redis-7.0.0b3/redis/asyncio/multidb/client.py +528 -0
  11. redis-7.0.0b3/redis/asyncio/multidb/command_executor.py +339 -0
  12. redis-7.0.0b3/redis/asyncio/multidb/config.py +210 -0
  13. redis-7.0.0b3/redis/asyncio/multidb/database.py +69 -0
  14. redis-7.0.0b3/redis/asyncio/multidb/event.py +84 -0
  15. redis-7.0.0b3/redis/asyncio/multidb/failover.py +125 -0
  16. redis-7.0.0b3/redis/asyncio/multidb/failure_detector.py +38 -0
  17. redis-7.0.0b3/redis/asyncio/multidb/healthcheck.py +292 -0
  18. redis-7.0.0b3/redis/background.py +204 -0
  19. {redis-7.0.0b1 → redis-7.0.0b3}/redis/client.py +41 -18
  20. {redis-7.0.0b1 → redis-7.0.0b3}/redis/cluster.py +5 -1
  21. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/core.py +10 -3
  22. {redis-7.0.0b1 → redis-7.0.0b3}/redis/connection.py +206 -151
  23. redis-7.0.0b3/redis/data_structure.py +81 -0
  24. {redis-7.0.0b1 → redis-7.0.0b3}/redis/event.py +84 -10
  25. redis-7.0.0b3/redis/http/http_client.py +425 -0
  26. redis-7.0.0b1/redis/maintenance_events.py → redis-7.0.0b3/redis/maint_notifications.py +154 -140
  27. redis-7.0.0b3/redis/multidb/circuit.py +144 -0
  28. redis-7.0.0b3/redis/multidb/client.py +524 -0
  29. redis-7.0.0b3/redis/multidb/command_executor.py +350 -0
  30. redis-7.0.0b3/redis/multidb/config.py +207 -0
  31. redis-7.0.0b3/redis/multidb/database.py +130 -0
  32. redis-7.0.0b3/redis/multidb/event.py +89 -0
  33. redis-7.0.0b3/redis/multidb/exception.py +17 -0
  34. redis-7.0.0b3/redis/multidb/failover.py +125 -0
  35. redis-7.0.0b3/redis/multidb/failure_detector.py +104 -0
  36. redis-7.0.0b3/redis/multidb/healthcheck.py +289 -0
  37. {redis-7.0.0b1 → redis-7.0.0b3}/redis/retry.py +14 -1
  38. {redis-7.0.0b1 → redis-7.0.0b3}/redis/utils.py +14 -0
  39. redis-7.0.0b3/tests/__init__.py +0 -0
  40. {redis-7.0.0b1 → redis-7.0.0b3}/tests/conftest.py +7 -0
  41. redis-7.0.0b3/tests/test_asyncio/__init__.py +0 -0
  42. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_commands.py +23 -0
  43. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_connection_pool.py +3 -0
  44. redis-7.0.0b3/tests/test_asyncio/test_multidb/__init__.py +0 -0
  45. redis-7.0.0b3/tests/test_asyncio/test_multidb/conftest.py +131 -0
  46. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_client.py +628 -0
  47. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_command_executor.py +181 -0
  48. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_config.py +166 -0
  49. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_failover.py +169 -0
  50. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_failure_detector.py +128 -0
  51. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_healthcheck.py +401 -0
  52. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_pipeline.py +488 -0
  53. redis-7.0.0b3/tests/test_asyncio/test_scenario/__init__.py +0 -0
  54. redis-7.0.0b3/tests/test_asyncio/test_scenario/conftest.py +116 -0
  55. redis-7.0.0b3/tests/test_asyncio/test_scenario/test_active_active.py +420 -0
  56. redis-7.0.0b3/tests/test_asyncio/test_ssl.py +143 -0
  57. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_vsets.py +2 -2
  58. redis-7.0.0b3/tests/test_auth/__init__.py +0 -0
  59. redis-7.0.0b3/tests/test_background.py +94 -0
  60. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_commands.py +51 -0
  61. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_connection_pool.py +62 -2
  62. redis-7.0.0b3/tests/test_data_structure.py +94 -0
  63. redis-7.0.0b3/tests/test_event.py +67 -0
  64. redis-7.0.0b3/tests/test_http/__init__.py +0 -0
  65. redis-7.0.0b3/tests/test_http/test_http_client.py +371 -0
  66. redis-7.0.0b3/tests/test_maint_notifications.py +893 -0
  67. redis-7.0.0b1/tests/test_maintenance_events_handling.py → redis-7.0.0b3/tests/test_maint_notifications_handling.py +379 -325
  68. redis-7.0.0b3/tests/test_multidb/__init__.py +0 -0
  69. redis-7.0.0b3/tests/test_multidb/conftest.py +131 -0
  70. redis-7.0.0b3/tests/test_multidb/test_circuit.py +57 -0
  71. redis-7.0.0b3/tests/test_multidb/test_client.py +615 -0
  72. redis-7.0.0b3/tests/test_multidb/test_command_executor.py +173 -0
  73. redis-7.0.0b3/tests/test_multidb/test_config.py +161 -0
  74. redis-7.0.0b3/tests/test_multidb/test_failover.py +161 -0
  75. redis-7.0.0b3/tests/test_multidb/test_failure_detector.py +117 -0
  76. redis-7.0.0b3/tests/test_multidb/test_healthcheck.py +385 -0
  77. redis-7.0.0b3/tests/test_multidb/test_pipeline.py +489 -0
  78. redis-7.0.0b3/tests/test_scenario/__init__.py +0 -0
  79. redis-7.0.0b3/tests/test_scenario/conftest.py +241 -0
  80. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_scenario/fault_injector_client.py +3 -2
  81. redis-7.0.0b1/tests/test_scenario/hitless_upgrade_helpers.py → redis-7.0.0b3/tests/test_scenario/maint_notifications_helpers.py +68 -14
  82. redis-7.0.0b3/tests/test_scenario/test_active_active.py +460 -0
  83. redis-7.0.0b1/tests/test_scenario/test_hitless_upgrade.py → redis-7.0.0b3/tests/test_scenario/test_maint_notifications.py +393 -92
  84. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_ssl.py +97 -0
  85. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_vsets.py +2 -2
  86. redis-7.0.0b1/tests/test_asyncio/test_ssl.py +0 -56
  87. redis-7.0.0b1/tests/test_maintenance_events.py +0 -869
  88. redis-7.0.0b1/tests/test_scenario/conftest.py +0 -120
  89. {redis-7.0.0b1 → redis-7.0.0b3}/.gitignore +0 -0
  90. {redis-7.0.0b1 → redis-7.0.0b3}/LICENSE +0 -0
  91. {redis-7.0.0b1 → redis-7.0.0b3}/README.md +0 -0
  92. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/__init__.py +0 -0
  93. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/commands.py +0 -0
  94. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/encoders.py +0 -0
  95. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/helpers.py +0 -0
  96. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/hiredis.py +0 -0
  97. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/resp2.py +0 -0
  98. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/resp3.py +0 -0
  99. {redis-7.0.0b1 → redis-7.0.0b3}/redis/_parsers/socket.py +0 -0
  100. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/__init__.py +0 -0
  101. {redis-7.0.0b1/redis/auth → redis-7.0.0b3/redis/asyncio/http}/__init__.py +0 -0
  102. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/lock.py +0 -0
  103. {redis-7.0.0b1/tests → redis-7.0.0b3/redis/asyncio/multidb}/__init__.py +0 -0
  104. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/retry.py +0 -0
  105. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/sentinel.py +0 -0
  106. {redis-7.0.0b1 → redis-7.0.0b3}/redis/asyncio/utils.py +0 -0
  107. {redis-7.0.0b1/tests/test_asyncio → redis-7.0.0b3/redis/auth}/__init__.py +0 -0
  108. {redis-7.0.0b1 → redis-7.0.0b3}/redis/auth/err.py +0 -0
  109. {redis-7.0.0b1 → redis-7.0.0b3}/redis/auth/idp.py +0 -0
  110. {redis-7.0.0b1 → redis-7.0.0b3}/redis/auth/token.py +0 -0
  111. {redis-7.0.0b1 → redis-7.0.0b3}/redis/auth/token_manager.py +0 -0
  112. {redis-7.0.0b1 → redis-7.0.0b3}/redis/backoff.py +0 -0
  113. {redis-7.0.0b1 → redis-7.0.0b3}/redis/cache.py +0 -0
  114. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/__init__.py +0 -0
  115. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/bf/__init__.py +0 -0
  116. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/bf/commands.py +0 -0
  117. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/bf/info.py +0 -0
  118. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/cluster.py +0 -0
  119. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/helpers.py +0 -0
  120. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/json/__init__.py +0 -0
  121. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/json/_util.py +0 -0
  122. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/json/commands.py +0 -0
  123. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/json/decoders.py +0 -0
  124. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/json/path.py +0 -0
  125. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/redismodules.py +0 -0
  126. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/__init__.py +0 -0
  127. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/_util.py +0 -0
  128. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/aggregation.py +0 -0
  129. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/commands.py +0 -0
  130. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/dialect.py +0 -0
  131. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/document.py +0 -0
  132. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/field.py +0 -0
  133. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/index_definition.py +0 -0
  134. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/profile_information.py +0 -0
  135. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/query.py +0 -0
  136. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/querystring.py +0 -0
  137. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/reducers.py +0 -0
  138. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/result.py +0 -0
  139. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/search/suggestion.py +0 -0
  140. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/sentinel.py +0 -0
  141. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/timeseries/__init__.py +0 -0
  142. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/timeseries/commands.py +0 -0
  143. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/timeseries/info.py +0 -0
  144. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/timeseries/utils.py +0 -0
  145. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/vectorset/__init__.py +0 -0
  146. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/vectorset/commands.py +0 -0
  147. {redis-7.0.0b1 → redis-7.0.0b3}/redis/commands/vectorset/utils.py +0 -0
  148. {redis-7.0.0b1 → redis-7.0.0b3}/redis/crc.py +0 -0
  149. {redis-7.0.0b1 → redis-7.0.0b3}/redis/credentials.py +0 -0
  150. {redis-7.0.0b1 → redis-7.0.0b3}/redis/exceptions.py +0 -0
  151. {redis-7.0.0b1/tests/test_auth → redis-7.0.0b3/redis/http}/__init__.py +0 -0
  152. {redis-7.0.0b1 → redis-7.0.0b3}/redis/lock.py +0 -0
  153. {redis-7.0.0b1/tests/test_scenario → redis-7.0.0b3/redis/multidb}/__init__.py +0 -0
  154. {redis-7.0.0b1 → redis-7.0.0b3}/redis/ocsp.py +0 -0
  155. {redis-7.0.0b1 → redis-7.0.0b3}/redis/py.typed +0 -0
  156. {redis-7.0.0b1 → redis-7.0.0b3}/redis/sentinel.py +0 -0
  157. {redis-7.0.0b1 → redis-7.0.0b3}/redis/typing.py +0 -0
  158. {redis-7.0.0b1 → redis-7.0.0b3}/tests/entraid_utils.py +0 -0
  159. {redis-7.0.0b1 → redis-7.0.0b3}/tests/mocks.py +0 -0
  160. {redis-7.0.0b1 → redis-7.0.0b3}/tests/ssl_utils.py +0 -0
  161. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/compat.py +0 -0
  162. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/conftest.py +0 -0
  163. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/mocks.py +0 -0
  164. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_bloom.py +0 -0
  165. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_cluster.py +0 -0
  166. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_cluster_transaction.py +0 -0
  167. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_connect.py +0 -0
  168. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_connection.py +0 -0
  169. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_credentials.py +0 -0
  170. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_cwe_404.py +0 -0
  171. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_encoding.py +0 -0
  172. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_hash.py +0 -0
  173. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_json.py +0 -0
  174. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_lock.py +0 -0
  175. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_monitor.py +0 -0
  176. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_pipeline.py +0 -0
  177. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_pubsub.py +0 -0
  178. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_retry.py +0 -0
  179. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_scripting.py +0 -0
  180. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_search.py +0 -0
  181. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_sentinel.py +0 -0
  182. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
  183. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_timeseries.py +0 -0
  184. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_usage_counter.py +0 -0
  185. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/test_utils.py +0 -0
  186. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
  187. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/testdata/titles.csv +0 -0
  188. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
  189. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_auth/test_token.py +0 -0
  190. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_auth/test_token_manager.py +0 -0
  191. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_backoff.py +0 -0
  192. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_bloom.py +0 -0
  193. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_cache.py +0 -0
  194. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_cluster.py +0 -0
  195. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_cluster_transaction.py +0 -0
  196. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_command_parser.py +0 -0
  197. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_connect.py +0 -0
  198. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_connection.py +0 -0
  199. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_credentials.py +0 -0
  200. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_encoding.py +0 -0
  201. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_function.py +0 -0
  202. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_hash.py +0 -0
  203. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_helpers.py +0 -0
  204. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_json.py +0 -0
  205. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_lock.py +0 -0
  206. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_max_connections_error.py +0 -0
  207. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_monitor.py +0 -0
  208. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_multiprocessing.py +0 -0
  209. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_parsers/test_helpers.py +0 -0
  210. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_pipeline.py +0 -0
  211. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_pubsub.py +0 -0
  212. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_retry.py +0 -0
  213. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_scripting.py +0 -0
  214. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_search.py +0 -0
  215. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_sentinel.py +0 -0
  216. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_sentinel_managed_connection.py +0 -0
  217. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_timeseries.py +0 -0
  218. {redis-7.0.0b1 → redis-7.0.0b3}/tests/test_utils.py +0 -0
  219. {redis-7.0.0b1 → redis-7.0.0b3}/tests/testdata/jsontestdata.py +0 -0
  220. {redis-7.0.0b1 → redis-7.0.0b3}/tests/testdata/titles.csv +0 -0
  221. {redis-7.0.0b1 → redis-7.0.0b3}/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.0b1
3
+ Version: 7.0.0b3
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
@@ -28,6 +28,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
28
28
  Classifier: Programming Language :: Python :: Implementation :: PyPy
29
29
  Requires-Python: >=3.9
30
30
  Requires-Dist: async-timeout>=4.0.3; python_full_version < '3.11.3'
31
+ Provides-Extra: circuit-breaker
32
+ Requires-Dist: pybreaker>=1.4.0; extra == 'circuit-breaker'
31
33
  Provides-Extra: hiredis
32
34
  Requires-Dist: hiredis>=3.2.0; extra == 'hiredis'
33
35
  Provides-Extra: jwt
@@ -14,3 +14,4 @@ uvloop
14
14
  vulture>=2.3.0
15
15
  numpy>=1.24.0
16
16
  redis-entraid==1.0.0
17
+ pybreaker>=1.4.0
@@ -42,6 +42,9 @@ ocsp = [
42
42
  jwt = [
43
43
  "PyJWT>=2.9.0",
44
44
  ]
45
+ circuit_breaker = [
46
+ "pybreaker>=1.4.0"
47
+ ]
45
48
 
46
49
  [project.urls]
47
50
  Changes = "https://github.com/redis/redis-py/releases"
@@ -80,6 +83,12 @@ filterwarnings = [
80
83
  # Ignore a coverage warning when COVERAGE_CORE=sysmon for Pythons < 3.12.
81
84
  "ignore:sys.monitoring isn't available:coverage.exceptions.CoverageWarning",
82
85
  ]
86
+ log_cli_level = "INFO"
87
+ log_cli_date_format = "%H:%M:%S:%f"
88
+ log_cli = false
89
+ log_cli_format = "%(asctime)s %(levelname)s %(threadName)s: %(message)s"
90
+ log_level = "INFO"
91
+ capture = "yes"
83
92
 
84
93
  [tool.ruff]
85
94
  target-version = "py39"
@@ -46,7 +46,7 @@ def int_or_str(value):
46
46
  return value
47
47
 
48
48
 
49
- __version__ = "7.0.0b1"
49
+ __version__ = "7.0.0b3"
50
50
  VERSION = tuple(map(int_or_str, __version__.split(".")))
51
51
 
52
52
 
@@ -4,13 +4,13 @@ from abc import ABC
4
4
  from asyncio import IncompleteReadError, StreamReader, TimeoutError
5
5
  from typing import Awaitable, Callable, List, Optional, Protocol, Union
6
6
 
7
- from redis.maintenance_events import (
8
- MaintenanceEvent,
9
- NodeFailedOverEvent,
10
- NodeFailingOverEvent,
11
- NodeMigratedEvent,
12
- NodeMigratingEvent,
13
- NodeMovingEvent,
7
+ from redis.maint_notifications import (
8
+ MaintenanceNotification,
9
+ NodeFailedOverNotification,
10
+ NodeFailingOverNotification,
11
+ NodeMigratedNotification,
12
+ NodeMigratingNotification,
13
+ NodeMovingNotification,
14
14
  )
15
15
 
16
16
  if sys.version_info.major >= 3 and sys.version_info.minor >= 11:
@@ -175,14 +175,14 @@ class MaintenanceNotificationsParser:
175
175
 
176
176
  @staticmethod
177
177
  def parse_maintenance_start_msg(response, notification_type):
178
- # Expected message format is: <event_type> <seq_number> <time>
178
+ # Expected message format is: <notification_type> <seq_number> <time>
179
179
  id = response[1]
180
180
  ttl = response[2]
181
181
  return notification_type(id, ttl)
182
182
 
183
183
  @staticmethod
184
184
  def parse_maintenance_completed_msg(response, notification_type):
185
- # Expected message format is: <event_type> <seq_number>
185
+ # Expected message format is: <notification_type> <seq_number>
186
186
  id = response[1]
187
187
  return notification_type(id)
188
188
 
@@ -200,7 +200,7 @@ class MaintenanceNotificationsParser:
200
200
  host, port = value.split(":")
201
201
  port = int(port) if port is not None else None
202
202
 
203
- return NodeMovingEvent(id, host, port, ttl)
203
+ return NodeMovingNotification(id, host, port, ttl)
204
204
 
205
205
 
206
206
  _INVALIDATION_MESSAGE = "invalidate"
@@ -217,25 +217,27 @@ _MAINTENANCE_MESSAGES = (
217
217
  _FAILED_OVER_MESSAGE,
218
218
  )
219
219
 
220
- MSG_TYPE_TO_EVENT_PARSER_MAPPING: dict[str, tuple[type[MaintenanceEvent], Callable]] = {
220
+ MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING: dict[
221
+ str, tuple[type[MaintenanceNotification], Callable]
222
+ ] = {
221
223
  _MIGRATING_MESSAGE: (
222
- NodeMigratingEvent,
224
+ NodeMigratingNotification,
223
225
  MaintenanceNotificationsParser.parse_maintenance_start_msg,
224
226
  ),
225
227
  _MIGRATED_MESSAGE: (
226
- NodeMigratedEvent,
228
+ NodeMigratedNotification,
227
229
  MaintenanceNotificationsParser.parse_maintenance_completed_msg,
228
230
  ),
229
231
  _FAILING_OVER_MESSAGE: (
230
- NodeFailingOverEvent,
232
+ NodeFailingOverNotification,
231
233
  MaintenanceNotificationsParser.parse_maintenance_start_msg,
232
234
  ),
233
235
  _FAILED_OVER_MESSAGE: (
234
- NodeFailedOverEvent,
236
+ NodeFailedOverNotification,
235
237
  MaintenanceNotificationsParser.parse_maintenance_completed_msg,
236
238
  ),
237
239
  _MOVING_MESSAGE: (
238
- NodeMovingEvent,
240
+ NodeMovingNotification,
239
241
  MaintenanceNotificationsParser.parse_moving_msg,
240
242
  ),
241
243
  }
@@ -273,14 +275,20 @@ class PushNotificationsParser(Protocol):
273
275
  return self.invalidation_push_handler_func(response)
274
276
 
275
277
  if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
276
- parser_function = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][1]
278
+ parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
279
+ msg_type
280
+ ][1]
277
281
 
278
282
  notification = parser_function(response)
279
283
  return self.node_moving_push_handler_func(notification)
280
284
 
281
285
  if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
282
- parser_function = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][1]
283
- notification_type = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][0]
286
+ parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
287
+ msg_type
288
+ ][1]
289
+ notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
290
+ msg_type
291
+ ][0]
284
292
  notification = parser_function(response, notification_type)
285
293
 
286
294
  if notification is not None:
@@ -342,13 +350,19 @@ class AsyncPushNotificationsParser(Protocol):
342
350
  msg_type = msg_type.decode()
343
351
 
344
352
  if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
345
- parser_function = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][1]
353
+ parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
354
+ msg_type
355
+ ][1]
346
356
  notification = parser_function(response)
347
357
  return await self.node_moving_push_handler_func(notification)
348
358
 
349
359
  if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
350
- parser_function = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][1]
351
- notification_type = MSG_TYPE_TO_EVENT_PARSER_MAPPING[msg_type][0]
360
+ parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
361
+ msg_type
362
+ ][1]
363
+ notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
364
+ msg_type
365
+ ][0]
352
366
  notification = parser_function(response, notification_type)
353
367
 
354
368
  if notification is not None:
@@ -81,10 +81,11 @@ from redis.utils import (
81
81
  )
82
82
 
83
83
  if TYPE_CHECKING and SSL_AVAILABLE:
84
- from ssl import TLSVersion, VerifyMode
84
+ from ssl import TLSVersion, VerifyFlags, VerifyMode
85
85
  else:
86
86
  TLSVersion = None
87
87
  VerifyMode = None
88
+ VerifyFlags = None
88
89
 
89
90
  PubSubHandler = Callable[[Dict[str, str]], Awaitable[None]]
90
91
  _KeyT = TypeVar("_KeyT", bound=KeyT)
@@ -238,6 +239,8 @@ class Redis(
238
239
  ssl_keyfile: Optional[str] = None,
239
240
  ssl_certfile: Optional[str] = None,
240
241
  ssl_cert_reqs: Union[str, VerifyMode] = "required",
242
+ ssl_include_verify_flags: Optional[List[VerifyFlags]] = None,
243
+ ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
241
244
  ssl_ca_certs: Optional[str] = None,
242
245
  ssl_ca_data: Optional[str] = None,
243
246
  ssl_check_hostname: bool = True,
@@ -347,6 +350,8 @@ class Redis(
347
350
  "ssl_keyfile": ssl_keyfile,
348
351
  "ssl_certfile": ssl_certfile,
349
352
  "ssl_cert_reqs": ssl_cert_reqs,
353
+ "ssl_include_verify_flags": ssl_include_verify_flags,
354
+ "ssl_exclude_verify_flags": ssl_exclude_verify_flags,
350
355
  "ssl_ca_certs": ssl_ca_certs,
351
356
  "ssl_ca_data": ssl_ca_data,
352
357
  "ssl_check_hostname": ssl_check_hostname,
@@ -1156,9 +1161,12 @@ class PubSub:
1156
1161
  return await self.handle_message(response, ignore_subscribe_messages)
1157
1162
  return None
1158
1163
 
1159
- def ping(self, message=None) -> Awaitable:
1164
+ def ping(self, message=None) -> Awaitable[bool]:
1160
1165
  """
1161
- Ping the Redis server
1166
+ Ping the Redis server to test connectivity.
1167
+
1168
+ Sends a PING command to the Redis server and returns True if the server
1169
+ responds with "PONG".
1162
1170
  """
1163
1171
  args = ["PING", message] if message is not None else ["PING"]
1164
1172
  return self.execute_command(*args)
@@ -1234,6 +1242,7 @@ class PubSub:
1234
1242
  *,
1235
1243
  exception_handler: Optional["PSWorkerThreadExcHandlerT"] = None,
1236
1244
  poll_timeout: float = 1.0,
1245
+ pubsub=None,
1237
1246
  ) -> None:
1238
1247
  """Process pub/sub messages using registered callbacks.
1239
1248
 
@@ -1258,9 +1267,14 @@ class PubSub:
1258
1267
  await self.connect()
1259
1268
  while True:
1260
1269
  try:
1261
- await self.get_message(
1262
- ignore_subscribe_messages=True, timeout=poll_timeout
1263
- )
1270
+ if pubsub is None:
1271
+ await self.get_message(
1272
+ ignore_subscribe_messages=True, timeout=poll_timeout
1273
+ )
1274
+ else:
1275
+ await pubsub.get_message(
1276
+ ignore_subscribe_messages=True, timeout=poll_timeout
1277
+ )
1264
1278
  except asyncio.CancelledError:
1265
1279
  raise
1266
1280
  except BaseException as e:
@@ -86,10 +86,11 @@ from redis.utils import (
86
86
  )
87
87
 
88
88
  if SSL_AVAILABLE:
89
- from ssl import TLSVersion, VerifyMode
89
+ from ssl import TLSVersion, VerifyFlags, VerifyMode
90
90
  else:
91
91
  TLSVersion = None
92
92
  VerifyMode = None
93
+ VerifyFlags = None
93
94
 
94
95
  TargetNodesT = TypeVar(
95
96
  "TargetNodesT", str, "ClusterNode", List["ClusterNode"], Dict[Any, "ClusterNode"]
@@ -299,6 +300,8 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
299
300
  ssl_ca_certs: Optional[str] = None,
300
301
  ssl_ca_data: Optional[str] = None,
301
302
  ssl_cert_reqs: Union[str, VerifyMode] = "required",
303
+ ssl_include_verify_flags: Optional[List[VerifyFlags]] = None,
304
+ ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
302
305
  ssl_certfile: Optional[str] = None,
303
306
  ssl_check_hostname: bool = True,
304
307
  ssl_keyfile: Optional[str] = None,
@@ -358,6 +361,8 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
358
361
  "ssl_ca_certs": ssl_ca_certs,
359
362
  "ssl_ca_data": ssl_ca_data,
360
363
  "ssl_cert_reqs": ssl_cert_reqs,
364
+ "ssl_include_verify_flags": ssl_include_verify_flags,
365
+ "ssl_exclude_verify_flags": ssl_exclude_verify_flags,
361
366
  "ssl_certfile": ssl_certfile,
362
367
  "ssl_check_hostname": ssl_check_hostname,
363
368
  "ssl_keyfile": ssl_keyfile,
@@ -404,6 +409,7 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
404
409
  else:
405
410
  self._event_dispatcher = event_dispatcher
406
411
 
412
+ self.startup_nodes = startup_nodes
407
413
  self.nodes_manager = NodesManager(
408
414
  startup_nodes,
409
415
  require_full_coverage,
@@ -2248,7 +2254,10 @@ class TransactionStrategy(AbstractStrategy):
2248
2254
  await self._pipe.cluster_client.nodes_manager.initialize()
2249
2255
  self.reinitialize_counter = 0
2250
2256
  else:
2251
- self._pipe.cluster_client.nodes_manager.update_moved_exception(error)
2257
+ if isinstance(error, AskError):
2258
+ self._pipe.cluster_client.nodes_manager.update_moved_exception(
2259
+ error
2260
+ )
2252
2261
 
2253
2262
  self._executing = False
2254
2263
 
@@ -30,11 +30,12 @@ from ..utils import SSL_AVAILABLE
30
30
 
31
31
  if SSL_AVAILABLE:
32
32
  import ssl
33
- from ssl import SSLContext, TLSVersion
33
+ from ssl import SSLContext, TLSVersion, VerifyFlags
34
34
  else:
35
35
  ssl = None
36
36
  TLSVersion = None
37
37
  SSLContext = None
38
+ VerifyFlags = None
38
39
 
39
40
  from ..auth.token import TokenInterface
40
41
  from ..event import AsyncAfterConnectionReleasedEvent, EventDispatcher
@@ -212,6 +213,7 @@ class AbstractConnection:
212
213
  self._connect_callbacks: List[weakref.WeakMethod[ConnectCallbackT]] = []
213
214
  self._buffer_cutoff = 6000
214
215
  self._re_auth_token: Optional[TokenInterface] = None
216
+ self._should_reconnect = False
215
217
 
216
218
  try:
217
219
  p = int(protocol)
@@ -342,6 +344,12 @@ class AbstractConnection:
342
344
  if task and inspect.isawaitable(task):
343
345
  await task
344
346
 
347
+ def mark_for_reconnect(self):
348
+ self._should_reconnect = True
349
+
350
+ def should_reconnect(self):
351
+ return self._should_reconnect
352
+
345
353
  @abstractmethod
346
354
  async def _connect(self):
347
355
  pass
@@ -793,6 +801,8 @@ class SSLConnection(Connection):
793
801
  ssl_keyfile: Optional[str] = None,
794
802
  ssl_certfile: Optional[str] = None,
795
803
  ssl_cert_reqs: Union[str, ssl.VerifyMode] = "required",
804
+ ssl_include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
805
+ ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
796
806
  ssl_ca_certs: Optional[str] = None,
797
807
  ssl_ca_data: Optional[str] = None,
798
808
  ssl_check_hostname: bool = True,
@@ -807,6 +817,8 @@ class SSLConnection(Connection):
807
817
  keyfile=ssl_keyfile,
808
818
  certfile=ssl_certfile,
809
819
  cert_reqs=ssl_cert_reqs,
820
+ include_verify_flags=ssl_include_verify_flags,
821
+ exclude_verify_flags=ssl_exclude_verify_flags,
810
822
  ca_certs=ssl_ca_certs,
811
823
  ca_data=ssl_ca_data,
812
824
  check_hostname=ssl_check_hostname,
@@ -832,6 +844,14 @@ class SSLConnection(Connection):
832
844
  def cert_reqs(self):
833
845
  return self.ssl_context.cert_reqs
834
846
 
847
+ @property
848
+ def include_verify_flags(self):
849
+ return self.ssl_context.include_verify_flags
850
+
851
+ @property
852
+ def exclude_verify_flags(self):
853
+ return self.ssl_context.exclude_verify_flags
854
+
835
855
  @property
836
856
  def ca_certs(self):
837
857
  return self.ssl_context.ca_certs
@@ -854,6 +874,8 @@ class RedisSSLContext:
854
874
  "keyfile",
855
875
  "certfile",
856
876
  "cert_reqs",
877
+ "include_verify_flags",
878
+ "exclude_verify_flags",
857
879
  "ca_certs",
858
880
  "ca_data",
859
881
  "context",
@@ -867,6 +889,8 @@ class RedisSSLContext:
867
889
  keyfile: Optional[str] = None,
868
890
  certfile: Optional[str] = None,
869
891
  cert_reqs: Optional[Union[str, ssl.VerifyMode]] = None,
892
+ include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
893
+ exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
870
894
  ca_certs: Optional[str] = None,
871
895
  ca_data: Optional[str] = None,
872
896
  check_hostname: bool = False,
@@ -892,6 +916,8 @@ class RedisSSLContext:
892
916
  )
893
917
  cert_reqs = CERT_REQS[cert_reqs]
894
918
  self.cert_reqs = cert_reqs
919
+ self.include_verify_flags = include_verify_flags
920
+ self.exclude_verify_flags = exclude_verify_flags
895
921
  self.ca_certs = ca_certs
896
922
  self.ca_data = ca_data
897
923
  self.check_hostname = (
@@ -906,6 +932,12 @@ class RedisSSLContext:
906
932
  context = ssl.create_default_context()
907
933
  context.check_hostname = self.check_hostname
908
934
  context.verify_mode = self.cert_reqs
935
+ if self.include_verify_flags:
936
+ for flag in self.include_verify_flags:
937
+ context.verify_flags |= flag
938
+ if self.exclude_verify_flags:
939
+ for flag in self.exclude_verify_flags:
940
+ context.verify_flags &= ~flag
909
941
  if self.certfile and self.keyfile:
910
942
  context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
911
943
  if self.ca_certs or self.ca_data:
@@ -953,6 +985,20 @@ def to_bool(value) -> Optional[bool]:
953
985
  return bool(value)
954
986
 
955
987
 
988
+ def parse_ssl_verify_flags(value):
989
+ # flags are passed in as a string representation of a list,
990
+ # e.g. VERIFY_X509_STRICT, VERIFY_X509_PARTIAL_CHAIN
991
+ verify_flags_str = value.replace("[", "").replace("]", "")
992
+
993
+ verify_flags = []
994
+ for flag in verify_flags_str.split(","):
995
+ flag = flag.strip()
996
+ if not hasattr(VerifyFlags, flag):
997
+ raise ValueError(f"Invalid ssl verify flag: {flag}")
998
+ verify_flags.append(getattr(VerifyFlags, flag))
999
+ return verify_flags
1000
+
1001
+
956
1002
  URL_QUERY_ARGUMENT_PARSERS: Mapping[str, Callable[..., object]] = MappingProxyType(
957
1003
  {
958
1004
  "db": int,
@@ -963,6 +1009,8 @@ URL_QUERY_ARGUMENT_PARSERS: Mapping[str, Callable[..., object]] = MappingProxyTy
963
1009
  "max_connections": int,
964
1010
  "health_check_interval": int,
965
1011
  "ssl_check_hostname": to_bool,
1012
+ "ssl_include_verify_flags": parse_ssl_verify_flags,
1013
+ "ssl_exclude_verify_flags": parse_ssl_verify_flags,
966
1014
  "timeout": float,
967
1015
  }
968
1016
  )
@@ -1021,6 +1069,7 @@ def parse_url(url: str) -> ConnectKwargs:
1021
1069
 
1022
1070
  if parsed.scheme == "rediss":
1023
1071
  kwargs["connection_class"] = SSLConnection
1072
+
1024
1073
  else:
1025
1074
  valid_schemes = "redis://, rediss://, unix://"
1026
1075
  raise ValueError(
@@ -1198,6 +1247,9 @@ class ConnectionPool:
1198
1247
  # Connections should always be returned to the correct pool,
1199
1248
  # not doing so is an error that will cause an exception here.
1200
1249
  self._in_use_connections.remove(connection)
1250
+ if connection.should_reconnect():
1251
+ await connection.disconnect()
1252
+
1201
1253
  self._available_connections.append(connection)
1202
1254
  await self._event_dispatcher.dispatch_async(
1203
1255
  AsyncAfterConnectionReleasedEvent(connection)
@@ -1225,6 +1277,14 @@ class ConnectionPool:
1225
1277
  if exc:
1226
1278
  raise exc
1227
1279
 
1280
+ async def update_active_connections_for_reconnect(self):
1281
+ """
1282
+ Mark all active connections for reconnect.
1283
+ """
1284
+ async with self._lock:
1285
+ for conn in self._in_use_connections:
1286
+ conn.mark_for_reconnect()
1287
+
1228
1288
  async def aclose(self) -> None:
1229
1289
  """Close the pool, disconnecting all connections"""
1230
1290
  await self.disconnect()