redis 7.0.0b2__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 (218) hide show
  1. {redis-7.0.0b2 → redis-7.0.0b3}/PKG-INFO +3 -1
  2. {redis-7.0.0b2 → redis-7.0.0b3}/dev_requirements.txt +1 -0
  3. {redis-7.0.0b2 → redis-7.0.0b3}/pyproject.toml +9 -0
  4. {redis-7.0.0b2 → redis-7.0.0b3}/redis/__init__.py +1 -1
  5. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/client.py +14 -5
  6. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/cluster.py +5 -1
  7. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/connection.py +18 -0
  8. redis-7.0.0b3/redis/asyncio/http/http_client.py +265 -0
  9. redis-7.0.0b3/redis/asyncio/multidb/client.py +528 -0
  10. redis-7.0.0b3/redis/asyncio/multidb/command_executor.py +339 -0
  11. redis-7.0.0b3/redis/asyncio/multidb/config.py +210 -0
  12. redis-7.0.0b3/redis/asyncio/multidb/database.py +69 -0
  13. redis-7.0.0b3/redis/asyncio/multidb/event.py +84 -0
  14. redis-7.0.0b3/redis/asyncio/multidb/failover.py +125 -0
  15. redis-7.0.0b3/redis/asyncio/multidb/failure_detector.py +38 -0
  16. redis-7.0.0b3/redis/asyncio/multidb/healthcheck.py +292 -0
  17. redis-7.0.0b3/redis/background.py +204 -0
  18. {redis-7.0.0b2 → redis-7.0.0b3}/redis/client.py +22 -3
  19. {redis-7.0.0b2 → redis-7.0.0b3}/redis/cluster.py +3 -1
  20. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/core.py +10 -3
  21. redis-7.0.0b3/redis/data_structure.py +81 -0
  22. {redis-7.0.0b2 → redis-7.0.0b3}/redis/event.py +84 -10
  23. redis-7.0.0b3/redis/http/http_client.py +425 -0
  24. redis-7.0.0b3/redis/multidb/circuit.py +144 -0
  25. redis-7.0.0b3/redis/multidb/client.py +524 -0
  26. redis-7.0.0b3/redis/multidb/command_executor.py +350 -0
  27. redis-7.0.0b3/redis/multidb/config.py +207 -0
  28. redis-7.0.0b3/redis/multidb/database.py +130 -0
  29. redis-7.0.0b3/redis/multidb/event.py +89 -0
  30. redis-7.0.0b3/redis/multidb/exception.py +17 -0
  31. redis-7.0.0b3/redis/multidb/failover.py +125 -0
  32. redis-7.0.0b3/redis/multidb/failure_detector.py +104 -0
  33. redis-7.0.0b3/redis/multidb/healthcheck.py +289 -0
  34. {redis-7.0.0b2 → redis-7.0.0b3}/redis/retry.py +14 -1
  35. {redis-7.0.0b2 → redis-7.0.0b3}/redis/utils.py +14 -0
  36. redis-7.0.0b3/tests/__init__.py +0 -0
  37. {redis-7.0.0b2 → redis-7.0.0b3}/tests/conftest.py +7 -0
  38. redis-7.0.0b3/tests/test_asyncio/__init__.py +0 -0
  39. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_commands.py +23 -0
  40. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_connection_pool.py +3 -0
  41. redis-7.0.0b3/tests/test_asyncio/test_multidb/__init__.py +0 -0
  42. redis-7.0.0b3/tests/test_asyncio/test_multidb/conftest.py +131 -0
  43. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_client.py +628 -0
  44. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_command_executor.py +181 -0
  45. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_config.py +166 -0
  46. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_failover.py +169 -0
  47. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_failure_detector.py +128 -0
  48. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_healthcheck.py +401 -0
  49. redis-7.0.0b3/tests/test_asyncio/test_multidb/test_pipeline.py +488 -0
  50. redis-7.0.0b3/tests/test_asyncio/test_scenario/__init__.py +0 -0
  51. redis-7.0.0b3/tests/test_asyncio/test_scenario/conftest.py +116 -0
  52. redis-7.0.0b3/tests/test_asyncio/test_scenario/test_active_active.py +420 -0
  53. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_vsets.py +2 -2
  54. redis-7.0.0b3/tests/test_auth/__init__.py +0 -0
  55. redis-7.0.0b3/tests/test_background.py +94 -0
  56. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_commands.py +51 -0
  57. redis-7.0.0b3/tests/test_data_structure.py +94 -0
  58. redis-7.0.0b3/tests/test_event.py +67 -0
  59. redis-7.0.0b3/tests/test_http/__init__.py +0 -0
  60. redis-7.0.0b3/tests/test_http/test_http_client.py +371 -0
  61. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_maint_notifications_handling.py +3 -0
  62. redis-7.0.0b3/tests/test_multidb/__init__.py +0 -0
  63. redis-7.0.0b3/tests/test_multidb/conftest.py +131 -0
  64. redis-7.0.0b3/tests/test_multidb/test_circuit.py +57 -0
  65. redis-7.0.0b3/tests/test_multidb/test_client.py +615 -0
  66. redis-7.0.0b3/tests/test_multidb/test_command_executor.py +173 -0
  67. redis-7.0.0b3/tests/test_multidb/test_config.py +161 -0
  68. redis-7.0.0b3/tests/test_multidb/test_failover.py +161 -0
  69. redis-7.0.0b3/tests/test_multidb/test_failure_detector.py +117 -0
  70. redis-7.0.0b3/tests/test_multidb/test_healthcheck.py +385 -0
  71. redis-7.0.0b3/tests/test_multidb/test_pipeline.py +489 -0
  72. redis-7.0.0b3/tests/test_scenario/__init__.py +0 -0
  73. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_scenario/conftest.py +119 -3
  74. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_scenario/maint_notifications_helpers.py +29 -14
  75. redis-7.0.0b3/tests/test_scenario/test_active_active.py +460 -0
  76. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_scenario/test_maint_notifications.py +9 -24
  77. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_vsets.py +2 -2
  78. {redis-7.0.0b2 → redis-7.0.0b3}/.gitignore +0 -0
  79. {redis-7.0.0b2 → redis-7.0.0b3}/LICENSE +0 -0
  80. {redis-7.0.0b2 → redis-7.0.0b3}/README.md +0 -0
  81. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/__init__.py +0 -0
  82. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/base.py +0 -0
  83. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/commands.py +0 -0
  84. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/encoders.py +0 -0
  85. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/helpers.py +0 -0
  86. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/hiredis.py +0 -0
  87. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/resp2.py +0 -0
  88. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/resp3.py +0 -0
  89. {redis-7.0.0b2 → redis-7.0.0b3}/redis/_parsers/socket.py +0 -0
  90. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/__init__.py +0 -0
  91. {redis-7.0.0b2/redis/auth → redis-7.0.0b3/redis/asyncio/http}/__init__.py +0 -0
  92. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/lock.py +0 -0
  93. {redis-7.0.0b2/tests → redis-7.0.0b3/redis/asyncio/multidb}/__init__.py +0 -0
  94. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/retry.py +0 -0
  95. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/sentinel.py +0 -0
  96. {redis-7.0.0b2 → redis-7.0.0b3}/redis/asyncio/utils.py +0 -0
  97. {redis-7.0.0b2/tests/test_asyncio → redis-7.0.0b3/redis/auth}/__init__.py +0 -0
  98. {redis-7.0.0b2 → redis-7.0.0b3}/redis/auth/err.py +0 -0
  99. {redis-7.0.0b2 → redis-7.0.0b3}/redis/auth/idp.py +0 -0
  100. {redis-7.0.0b2 → redis-7.0.0b3}/redis/auth/token.py +0 -0
  101. {redis-7.0.0b2 → redis-7.0.0b3}/redis/auth/token_manager.py +0 -0
  102. {redis-7.0.0b2 → redis-7.0.0b3}/redis/backoff.py +0 -0
  103. {redis-7.0.0b2 → redis-7.0.0b3}/redis/cache.py +0 -0
  104. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/__init__.py +0 -0
  105. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/bf/__init__.py +0 -0
  106. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/bf/commands.py +0 -0
  107. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/bf/info.py +0 -0
  108. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/cluster.py +0 -0
  109. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/helpers.py +0 -0
  110. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/json/__init__.py +0 -0
  111. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/json/_util.py +0 -0
  112. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/json/commands.py +0 -0
  113. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/json/decoders.py +0 -0
  114. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/json/path.py +0 -0
  115. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/redismodules.py +0 -0
  116. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/__init__.py +0 -0
  117. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/_util.py +0 -0
  118. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/aggregation.py +0 -0
  119. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/commands.py +0 -0
  120. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/dialect.py +0 -0
  121. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/document.py +0 -0
  122. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/field.py +0 -0
  123. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/index_definition.py +0 -0
  124. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/profile_information.py +0 -0
  125. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/query.py +0 -0
  126. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/querystring.py +0 -0
  127. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/reducers.py +0 -0
  128. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/result.py +0 -0
  129. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/search/suggestion.py +0 -0
  130. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/sentinel.py +0 -0
  131. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/timeseries/__init__.py +0 -0
  132. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/timeseries/commands.py +0 -0
  133. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/timeseries/info.py +0 -0
  134. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/timeseries/utils.py +0 -0
  135. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/vectorset/__init__.py +0 -0
  136. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/vectorset/commands.py +0 -0
  137. {redis-7.0.0b2 → redis-7.0.0b3}/redis/commands/vectorset/utils.py +0 -0
  138. {redis-7.0.0b2 → redis-7.0.0b3}/redis/connection.py +0 -0
  139. {redis-7.0.0b2 → redis-7.0.0b3}/redis/crc.py +0 -0
  140. {redis-7.0.0b2 → redis-7.0.0b3}/redis/credentials.py +0 -0
  141. {redis-7.0.0b2 → redis-7.0.0b3}/redis/exceptions.py +0 -0
  142. {redis-7.0.0b2/tests/test_auth → redis-7.0.0b3/redis/http}/__init__.py +0 -0
  143. {redis-7.0.0b2 → redis-7.0.0b3}/redis/lock.py +0 -0
  144. {redis-7.0.0b2 → redis-7.0.0b3}/redis/maint_notifications.py +0 -0
  145. {redis-7.0.0b2/tests/test_scenario → redis-7.0.0b3/redis/multidb}/__init__.py +0 -0
  146. {redis-7.0.0b2 → redis-7.0.0b3}/redis/ocsp.py +0 -0
  147. {redis-7.0.0b2 → redis-7.0.0b3}/redis/py.typed +0 -0
  148. {redis-7.0.0b2 → redis-7.0.0b3}/redis/sentinel.py +0 -0
  149. {redis-7.0.0b2 → redis-7.0.0b3}/redis/typing.py +0 -0
  150. {redis-7.0.0b2 → redis-7.0.0b3}/tests/entraid_utils.py +0 -0
  151. {redis-7.0.0b2 → redis-7.0.0b3}/tests/mocks.py +0 -0
  152. {redis-7.0.0b2 → redis-7.0.0b3}/tests/ssl_utils.py +0 -0
  153. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/compat.py +0 -0
  154. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/conftest.py +0 -0
  155. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/mocks.py +0 -0
  156. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_bloom.py +0 -0
  157. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_cluster.py +0 -0
  158. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_cluster_transaction.py +0 -0
  159. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_connect.py +0 -0
  160. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_connection.py +0 -0
  161. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_credentials.py +0 -0
  162. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_cwe_404.py +0 -0
  163. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_encoding.py +0 -0
  164. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_hash.py +0 -0
  165. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_json.py +0 -0
  166. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_lock.py +0 -0
  167. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_monitor.py +0 -0
  168. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_pipeline.py +0 -0
  169. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_pubsub.py +0 -0
  170. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_retry.py +0 -0
  171. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_scripting.py +0 -0
  172. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_search.py +0 -0
  173. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_sentinel.py +0 -0
  174. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
  175. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_ssl.py +0 -0
  176. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_timeseries.py +0 -0
  177. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_usage_counter.py +0 -0
  178. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/test_utils.py +0 -0
  179. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
  180. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/testdata/titles.csv +0 -0
  181. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
  182. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_auth/test_token.py +0 -0
  183. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_auth/test_token_manager.py +0 -0
  184. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_backoff.py +0 -0
  185. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_bloom.py +0 -0
  186. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_cache.py +0 -0
  187. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_cluster.py +0 -0
  188. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_cluster_transaction.py +0 -0
  189. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_command_parser.py +0 -0
  190. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_connect.py +0 -0
  191. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_connection.py +0 -0
  192. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_connection_pool.py +0 -0
  193. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_credentials.py +0 -0
  194. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_encoding.py +0 -0
  195. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_function.py +0 -0
  196. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_hash.py +0 -0
  197. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_helpers.py +0 -0
  198. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_json.py +0 -0
  199. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_lock.py +0 -0
  200. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_maint_notifications.py +0 -0
  201. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_max_connections_error.py +0 -0
  202. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_monitor.py +0 -0
  203. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_multiprocessing.py +0 -0
  204. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_parsers/test_helpers.py +0 -0
  205. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_pipeline.py +0 -0
  206. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_pubsub.py +0 -0
  207. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_retry.py +0 -0
  208. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_scenario/fault_injector_client.py +0 -0
  209. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_scripting.py +0 -0
  210. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_search.py +0 -0
  211. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_sentinel.py +0 -0
  212. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_sentinel_managed_connection.py +0 -0
  213. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_ssl.py +0 -0
  214. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_timeseries.py +0 -0
  215. {redis-7.0.0b2 → redis-7.0.0b3}/tests/test_utils.py +0 -0
  216. {redis-7.0.0b2 → redis-7.0.0b3}/tests/testdata/jsontestdata.py +0 -0
  217. {redis-7.0.0b2 → redis-7.0.0b3}/tests/testdata/titles.csv +0 -0
  218. {redis-7.0.0b2 → 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.0b2
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.0b2"
49
+ __version__ = "7.0.0b3"
50
50
  VERSION = tuple(map(int_or_str, __version__.split(".")))
51
51
 
52
52
 
@@ -1161,9 +1161,12 @@ class PubSub:
1161
1161
  return await self.handle_message(response, ignore_subscribe_messages)
1162
1162
  return None
1163
1163
 
1164
- def ping(self, message=None) -> Awaitable:
1164
+ def ping(self, message=None) -> Awaitable[bool]:
1165
1165
  """
1166
- 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".
1167
1170
  """
1168
1171
  args = ["PING", message] if message is not None else ["PING"]
1169
1172
  return self.execute_command(*args)
@@ -1239,6 +1242,7 @@ class PubSub:
1239
1242
  *,
1240
1243
  exception_handler: Optional["PSWorkerThreadExcHandlerT"] = None,
1241
1244
  poll_timeout: float = 1.0,
1245
+ pubsub=None,
1242
1246
  ) -> None:
1243
1247
  """Process pub/sub messages using registered callbacks.
1244
1248
 
@@ -1263,9 +1267,14 @@ class PubSub:
1263
1267
  await self.connect()
1264
1268
  while True:
1265
1269
  try:
1266
- await self.get_message(
1267
- ignore_subscribe_messages=True, timeout=poll_timeout
1268
- )
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
+ )
1269
1278
  except asyncio.CancelledError:
1270
1279
  raise
1271
1280
  except BaseException as e:
@@ -409,6 +409,7 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
409
409
  else:
410
410
  self._event_dispatcher = event_dispatcher
411
411
 
412
+ self.startup_nodes = startup_nodes
412
413
  self.nodes_manager = NodesManager(
413
414
  startup_nodes,
414
415
  require_full_coverage,
@@ -2253,7 +2254,10 @@ class TransactionStrategy(AbstractStrategy):
2253
2254
  await self._pipe.cluster_client.nodes_manager.initialize()
2254
2255
  self.reinitialize_counter = 0
2255
2256
  else:
2256
- 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
+ )
2257
2261
 
2258
2262
  self._executing = False
2259
2263
 
@@ -213,6 +213,7 @@ class AbstractConnection:
213
213
  self._connect_callbacks: List[weakref.WeakMethod[ConnectCallbackT]] = []
214
214
  self._buffer_cutoff = 6000
215
215
  self._re_auth_token: Optional[TokenInterface] = None
216
+ self._should_reconnect = False
216
217
 
217
218
  try:
218
219
  p = int(protocol)
@@ -343,6 +344,12 @@ class AbstractConnection:
343
344
  if task and inspect.isawaitable(task):
344
345
  await task
345
346
 
347
+ def mark_for_reconnect(self):
348
+ self._should_reconnect = True
349
+
350
+ def should_reconnect(self):
351
+ return self._should_reconnect
352
+
346
353
  @abstractmethod
347
354
  async def _connect(self):
348
355
  pass
@@ -1240,6 +1247,9 @@ class ConnectionPool:
1240
1247
  # Connections should always be returned to the correct pool,
1241
1248
  # not doing so is an error that will cause an exception here.
1242
1249
  self._in_use_connections.remove(connection)
1250
+ if connection.should_reconnect():
1251
+ await connection.disconnect()
1252
+
1243
1253
  self._available_connections.append(connection)
1244
1254
  await self._event_dispatcher.dispatch_async(
1245
1255
  AsyncAfterConnectionReleasedEvent(connection)
@@ -1267,6 +1277,14 @@ class ConnectionPool:
1267
1277
  if exc:
1268
1278
  raise exc
1269
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
+
1270
1288
  async def aclose(self) -> None:
1271
1289
  """Close the pool, disconnecting all connections"""
1272
1290
  await self.disconnect()
@@ -0,0 +1,265 @@
1
+ import asyncio
2
+ from abc import ABC, abstractmethod
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from typing import Any, Mapping, Optional, Union
5
+
6
+ from redis.http.http_client import HttpClient, HttpResponse
7
+
8
+ DEFAULT_USER_AGENT = "HttpClient/1.0 (+https://example.invalid)"
9
+ DEFAULT_TIMEOUT = 30.0
10
+ RETRY_STATUS_CODES = {429, 500, 502, 503, 504}
11
+
12
+
13
+ class AsyncHTTPClient(ABC):
14
+ @abstractmethod
15
+ async def get(
16
+ self,
17
+ path: str,
18
+ params: Optional[
19
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
20
+ ] = None,
21
+ headers: Optional[Mapping[str, str]] = None,
22
+ timeout: Optional[float] = None,
23
+ expect_json: bool = True,
24
+ ) -> Union[HttpResponse, Any]:
25
+ """
26
+ Invoke HTTP GET request."""
27
+ pass
28
+
29
+ @abstractmethod
30
+ async def delete(
31
+ self,
32
+ path: str,
33
+ params: Optional[
34
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
35
+ ] = None,
36
+ headers: Optional[Mapping[str, str]] = None,
37
+ timeout: Optional[float] = None,
38
+ expect_json: bool = True,
39
+ ) -> Union[HttpResponse, Any]:
40
+ """
41
+ Invoke HTTP DELETE request."""
42
+ pass
43
+
44
+ @abstractmethod
45
+ async def post(
46
+ self,
47
+ path: str,
48
+ json_body: Optional[Any] = None,
49
+ data: Optional[Union[bytes, str]] = None,
50
+ params: Optional[
51
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
52
+ ] = None,
53
+ headers: Optional[Mapping[str, str]] = None,
54
+ timeout: Optional[float] = None,
55
+ expect_json: bool = True,
56
+ ) -> Union[HttpResponse, Any]:
57
+ """
58
+ Invoke HTTP POST request."""
59
+ pass
60
+
61
+ @abstractmethod
62
+ async def put(
63
+ self,
64
+ path: str,
65
+ json_body: Optional[Any] = None,
66
+ data: Optional[Union[bytes, str]] = None,
67
+ params: Optional[
68
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
69
+ ] = None,
70
+ headers: Optional[Mapping[str, str]] = None,
71
+ timeout: Optional[float] = None,
72
+ expect_json: bool = True,
73
+ ) -> Union[HttpResponse, Any]:
74
+ """
75
+ Invoke HTTP PUT request."""
76
+ pass
77
+
78
+ @abstractmethod
79
+ async def patch(
80
+ self,
81
+ path: str,
82
+ json_body: Optional[Any] = None,
83
+ data: Optional[Union[bytes, str]] = None,
84
+ params: Optional[
85
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
86
+ ] = None,
87
+ headers: Optional[Mapping[str, str]] = None,
88
+ timeout: Optional[float] = None,
89
+ expect_json: bool = True,
90
+ ) -> Union[HttpResponse, Any]:
91
+ """
92
+ Invoke HTTP PATCH request."""
93
+ pass
94
+
95
+ @abstractmethod
96
+ async def request(
97
+ self,
98
+ method: str,
99
+ path: str,
100
+ params: Optional[
101
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
102
+ ] = None,
103
+ headers: Optional[Mapping[str, str]] = None,
104
+ body: Optional[Union[bytes, str]] = None,
105
+ timeout: Optional[float] = None,
106
+ ) -> HttpResponse:
107
+ """
108
+ Invoke HTTP request with given method."""
109
+ pass
110
+
111
+
112
+ class AsyncHTTPClientWrapper(AsyncHTTPClient):
113
+ """
114
+ An async wrapper around sync HTTP client with thread pool execution.
115
+ """
116
+
117
+ def __init__(self, client: HttpClient, max_workers: int = 10) -> None:
118
+ """
119
+ Initialize a new HTTP client instance.
120
+
121
+ Args:
122
+ client: Sync HTTP client instance.
123
+ max_workers: Maximum number of concurrent requests.
124
+
125
+ The client supports both regular HTTPS with server verification and mutual TLS
126
+ authentication. For server verification, provide CA certificate information via
127
+ ca_file, ca_path or ca_data. For mutual TLS, additionally provide a client
128
+ certificate and key via client_cert_file and client_key_file.
129
+ """
130
+ self.client = client
131
+ self._executor = ThreadPoolExecutor(max_workers=max_workers)
132
+
133
+ async def get(
134
+ self,
135
+ path: str,
136
+ params: Optional[
137
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
138
+ ] = None,
139
+ headers: Optional[Mapping[str, str]] = None,
140
+ timeout: Optional[float] = None,
141
+ expect_json: bool = True,
142
+ ) -> Union[HttpResponse, Any]:
143
+ loop = asyncio.get_event_loop()
144
+ return await loop.run_in_executor(
145
+ self._executor, self.client.get, path, params, headers, timeout, expect_json
146
+ )
147
+
148
+ async def delete(
149
+ self,
150
+ path: str,
151
+ params: Optional[
152
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
153
+ ] = None,
154
+ headers: Optional[Mapping[str, str]] = None,
155
+ timeout: Optional[float] = None,
156
+ expect_json: bool = True,
157
+ ) -> Union[HttpResponse, Any]:
158
+ loop = asyncio.get_event_loop()
159
+ return await loop.run_in_executor(
160
+ self._executor,
161
+ self.client.delete,
162
+ path,
163
+ params,
164
+ headers,
165
+ timeout,
166
+ expect_json,
167
+ )
168
+
169
+ async def post(
170
+ self,
171
+ path: str,
172
+ json_body: Optional[Any] = None,
173
+ data: Optional[Union[bytes, str]] = None,
174
+ params: Optional[
175
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
176
+ ] = None,
177
+ headers: Optional[Mapping[str, str]] = None,
178
+ timeout: Optional[float] = None,
179
+ expect_json: bool = True,
180
+ ) -> Union[HttpResponse, Any]:
181
+ loop = asyncio.get_event_loop()
182
+ return await loop.run_in_executor(
183
+ self._executor,
184
+ self.client.post,
185
+ path,
186
+ json_body,
187
+ data,
188
+ params,
189
+ headers,
190
+ timeout,
191
+ expect_json,
192
+ )
193
+
194
+ async def put(
195
+ self,
196
+ path: str,
197
+ json_body: Optional[Any] = None,
198
+ data: Optional[Union[bytes, str]] = None,
199
+ params: Optional[
200
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
201
+ ] = None,
202
+ headers: Optional[Mapping[str, str]] = None,
203
+ timeout: Optional[float] = None,
204
+ expect_json: bool = True,
205
+ ) -> Union[HttpResponse, Any]:
206
+ loop = asyncio.get_event_loop()
207
+ return await loop.run_in_executor(
208
+ self._executor,
209
+ self.client.put,
210
+ path,
211
+ json_body,
212
+ data,
213
+ params,
214
+ headers,
215
+ timeout,
216
+ expect_json,
217
+ )
218
+
219
+ async def patch(
220
+ self,
221
+ path: str,
222
+ json_body: Optional[Any] = None,
223
+ data: Optional[Union[bytes, str]] = None,
224
+ params: Optional[
225
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
226
+ ] = None,
227
+ headers: Optional[Mapping[str, str]] = None,
228
+ timeout: Optional[float] = None,
229
+ expect_json: bool = True,
230
+ ) -> Union[HttpResponse, Any]:
231
+ loop = asyncio.get_event_loop()
232
+ return await loop.run_in_executor(
233
+ self._executor,
234
+ self.client.patch,
235
+ path,
236
+ json_body,
237
+ data,
238
+ params,
239
+ headers,
240
+ timeout,
241
+ expect_json,
242
+ )
243
+
244
+ async def request(
245
+ self,
246
+ method: str,
247
+ path: str,
248
+ params: Optional[
249
+ Mapping[str, Union[None, str, int, float, bool, list, tuple]]
250
+ ] = None,
251
+ headers: Optional[Mapping[str, str]] = None,
252
+ body: Optional[Union[bytes, str]] = None,
253
+ timeout: Optional[float] = None,
254
+ ) -> HttpResponse:
255
+ loop = asyncio.get_event_loop()
256
+ return await loop.run_in_executor(
257
+ self._executor,
258
+ self.client.request,
259
+ method,
260
+ path,
261
+ params,
262
+ headers,
263
+ body,
264
+ timeout,
265
+ )