Skip to content

Commit 183fed7

Browse files
authored
Merge branch 'master' into temp-disable-aa
2 parents 314cc99 + 0894218 commit 183fed7

24 files changed

+382
-79
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636

3737
steps:
3838
- name: Checkout repository
39-
uses: actions/checkout@v5
39+
uses: actions/checkout@v6
4040

4141
# Initializes the CodeQL tools for scanning.
4242
- name: Initialize CodeQL

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
name: Build docs
2626
runs-on: ubuntu-latest
2727
steps:
28-
- uses: actions/checkout@v5
28+
- uses: actions/checkout@v6
2929
- uses: actions/setup-python@v6
3030
with:
3131
python-version: "3.10"

.github/workflows/hiredis-py-integration.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
5454
name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}} (${{ matrix.hiredis-version }}); EL:${{matrix.event-loop}}
5555
steps:
56-
- uses: actions/checkout@v5
56+
- uses: actions/checkout@v6
5757
with:
5858
ref: ${{ inputs.redis-py-branch }}
5959
- name: Run tests

.github/workflows/integration.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
name: Dependency audit
3838
runs-on: ubuntu-latest
3939
steps:
40-
- uses: actions/checkout@v5
40+
- uses: actions/checkout@v6
4141
- uses: pypa/[email protected]
4242
with:
4343
inputs: dev_requirements.txt
@@ -48,7 +48,7 @@ jobs:
4848
name: Code linters
4949
runs-on: ubuntu-latest
5050
steps:
51-
- uses: actions/checkout@v5
51+
- uses: actions/checkout@v6
5252
- uses: actions/setup-python@v6
5353
with:
5454
python-version: "3.10"
@@ -84,7 +84,7 @@ jobs:
8484
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
8585
name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}
8686
steps:
87-
- uses: actions/checkout@v5
87+
- uses: actions/checkout@v6
8888
- name: Run tests
8989
uses: ./.github/actions/run-tests
9090
with:
@@ -108,7 +108,7 @@ jobs:
108108
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
109109
name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}
110110
steps:
111-
- uses: actions/checkout@v5
111+
- uses: actions/checkout@v6
112112
- name: Run tests
113113
uses: ./.github/actions/run-tests
114114
with:
@@ -133,7 +133,7 @@ jobs:
133133
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
134134
name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}} (${{ matrix.hiredis-version }}); EL:${{matrix.event-loop}}
135135
steps:
136-
- uses: actions/checkout@v5
136+
- uses: actions/checkout@v6
137137
- name: Run tests
138138
uses: ./.github/actions/run-tests
139139
with:
@@ -158,7 +158,7 @@ jobs:
158158
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
159159
name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}
160160
steps:
161-
- uses: actions/checkout@v5
161+
- uses: actions/checkout@v6
162162
- name: Run tests
163163
uses: ./.github/actions/run-tests
164164
with:
@@ -176,7 +176,7 @@ jobs:
176176
matrix:
177177
extension: ['tar.gz', 'whl']
178178
steps:
179-
- uses: actions/checkout@v5
179+
- uses: actions/checkout@v6
180180
- uses: actions/setup-python@v6
181181
with:
182182
python-version: "3.10"
@@ -195,7 +195,7 @@ jobs:
195195
matrix:
196196
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', 'pypy-3.10', 'pypy-3.11']
197197
steps:
198-
- uses: actions/checkout@v5
198+
- uses: actions/checkout@v6
199199
- uses: actions/setup-python@v6
200200
with:
201201
python-version: ${{ matrix.python-version }}

.github/workflows/pypi-publish.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
build_and_package:
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v5
16+
- uses: actions/checkout@v6
1717
- name: install python
1818
uses: actions/setup-python@v6
1919
with:

.github/workflows/spellcheck.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ jobs:
66
runs-on: ubuntu-latest
77
steps:
88
- name: Checkout
9-
uses: actions/checkout@v5
9+
uses: actions/checkout@v6
1010
- name: Check Spelling
11-
uses: rojopolis/spellcheck-github-actions@0.53.0
11+
uses: rojopolis/spellcheck-github-actions@0.55.0
1212
with:
1313
config_path: .github/spellcheck-settings.yml
1414
task_name: Markdown

dev_requirements.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ build
22
build==1.2.2.post1 ; platform_python_implementation == "PyPy"
33
click==8.0.4
44
invoke==2.2.0
5-
mock
6-
mock==5.1.0 ; platform_python_implementation == "PyPy"
75
packaging>=20.4
86
packaging==24.2 ; platform_python_implementation == "PyPy"
97

redis/asyncio/client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,12 @@ class Redis(
126126

127127
@classmethod
128128
def from_url(
129-
cls,
129+
cls: Type["Redis"],
130130
url: str,
131131
single_connection_client: bool = False,
132132
auto_close_connection_pool: Optional[bool] = None,
133133
**kwargs,
134-
):
134+
) -> "Redis":
135135
"""
136136
Return a Redis client object configured from the given URL
137137
@@ -243,9 +243,11 @@ def __init__(
243243
ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
244244
ssl_ca_certs: Optional[str] = None,
245245
ssl_ca_data: Optional[str] = None,
246+
ssl_ca_path: Optional[str] = None,
246247
ssl_check_hostname: bool = True,
247248
ssl_min_version: Optional[TLSVersion] = None,
248249
ssl_ciphers: Optional[str] = None,
250+
ssl_password: Optional[str] = None,
249251
max_connections: Optional[int] = None,
250252
single_connection_client: bool = False,
251253
health_check_interval: int = 0,
@@ -354,9 +356,11 @@ def __init__(
354356
"ssl_exclude_verify_flags": ssl_exclude_verify_flags,
355357
"ssl_ca_certs": ssl_ca_certs,
356358
"ssl_ca_data": ssl_ca_data,
359+
"ssl_ca_path": ssl_ca_path,
357360
"ssl_check_hostname": ssl_check_hostname,
358361
"ssl_min_version": ssl_min_version,
359362
"ssl_ciphers": ssl_ciphers,
363+
"ssl_password": ssl_password,
360364
}
361365
)
362366
# This arg only used if no pool is passed in

redis/asyncio/connection.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
AuthenticationWrongNumberOfArgsError,
5858
ConnectionError,
5959
DataError,
60+
MaxConnectionsError,
6061
RedisError,
6162
ResponseError,
6263
TimeoutError,
@@ -295,7 +296,14 @@ def set_parser(self, parser_class: Type[BaseParser]) -> None:
295296

296297
async def connect(self):
297298
"""Connects to the Redis server if not already connected"""
298-
await self.connect_check_health(check_health=True)
299+
# try once the socket connect with the handshake, retry the whole
300+
# connect/handshake flow based on retry policy
301+
await self.retry.call_with_retry(
302+
lambda: self.connect_check_health(
303+
check_health=True, retry_socket_connect=False
304+
),
305+
lambda error: self.disconnect(),
306+
)
299307

300308
async def connect_check_health(
301309
self, check_health: bool = True, retry_socket_connect: bool = True
@@ -805,9 +813,11 @@ def __init__(
805813
ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
806814
ssl_ca_certs: Optional[str] = None,
807815
ssl_ca_data: Optional[str] = None,
816+
ssl_ca_path: Optional[str] = None,
808817
ssl_check_hostname: bool = True,
809818
ssl_min_version: Optional[TLSVersion] = None,
810819
ssl_ciphers: Optional[str] = None,
820+
ssl_password: Optional[str] = None,
811821
**kwargs,
812822
):
813823
if not SSL_AVAILABLE:
@@ -821,9 +831,11 @@ def __init__(
821831
exclude_verify_flags=ssl_exclude_verify_flags,
822832
ca_certs=ssl_ca_certs,
823833
ca_data=ssl_ca_data,
834+
ca_path=ssl_ca_path,
824835
check_hostname=ssl_check_hostname,
825836
min_version=ssl_min_version,
826837
ciphers=ssl_ciphers,
838+
password=ssl_password,
827839
)
828840
super().__init__(**kwargs)
829841

@@ -878,10 +890,12 @@ class RedisSSLContext:
878890
"exclude_verify_flags",
879891
"ca_certs",
880892
"ca_data",
893+
"ca_path",
881894
"context",
882895
"check_hostname",
883896
"min_version",
884897
"ciphers",
898+
"password",
885899
)
886900

887901
def __init__(
@@ -893,9 +907,11 @@ def __init__(
893907
exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
894908
ca_certs: Optional[str] = None,
895909
ca_data: Optional[str] = None,
910+
ca_path: Optional[str] = None,
896911
check_hostname: bool = False,
897912
min_version: Optional[TLSVersion] = None,
898913
ciphers: Optional[str] = None,
914+
password: Optional[str] = None,
899915
):
900916
if not SSL_AVAILABLE:
901917
raise RedisError("Python wasn't built with SSL support")
@@ -920,11 +936,13 @@ def __init__(
920936
self.exclude_verify_flags = exclude_verify_flags
921937
self.ca_certs = ca_certs
922938
self.ca_data = ca_data
939+
self.ca_path = ca_path
923940
self.check_hostname = (
924941
check_hostname if self.cert_reqs != ssl.CERT_NONE else False
925942
)
926943
self.min_version = min_version
927944
self.ciphers = ciphers
945+
self.password = password
928946
self.context: Optional[SSLContext] = None
929947

930948
def get(self) -> SSLContext:
@@ -938,10 +956,16 @@ def get(self) -> SSLContext:
938956
if self.exclude_verify_flags:
939957
for flag in self.exclude_verify_flags:
940958
context.verify_flags &= ~flag
941-
if self.certfile and self.keyfile:
942-
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
943-
if self.ca_certs or self.ca_data:
944-
context.load_verify_locations(cafile=self.ca_certs, cadata=self.ca_data)
959+
if self.certfile or self.keyfile:
960+
context.load_cert_chain(
961+
certfile=self.certfile,
962+
keyfile=self.keyfile,
963+
password=self.password,
964+
)
965+
if self.ca_certs or self.ca_data or self.ca_path:
966+
context.load_verify_locations(
967+
cafile=self.ca_certs, capath=self.ca_path, cadata=self.ca_data
968+
)
945969
if self.min_version is not None:
946970
context.minimum_version = self.min_version
947971
if self.ciphers is not None:
@@ -1208,7 +1232,7 @@ def get_available_connection(self):
12081232
connection = self._available_connections.pop()
12091233
except IndexError:
12101234
if len(self._in_use_connections) >= self.max_connections:
1211-
raise ConnectionError("Too many connections") from None
1235+
raise MaxConnectionsError("Too many connections") from None
12121236
connection = self.make_connection()
12131237
self._in_use_connections.add(connection)
12141238
return connection

redis/asyncio/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
from typing import TYPE_CHECKING
1+
from typing import TYPE_CHECKING, Any
22

33
if TYPE_CHECKING:
44
from redis.asyncio.client import Pipeline, Redis
55

66

7-
def from_url(url, **kwargs):
7+
def from_url(url: str, **kwargs: Any) -> "Redis":
88
"""
99
Returns an active Redis client generated from the given database URL.
1010

0 commit comments

Comments
 (0)