cloud-sql-python-connector 1.20.4__tar.gz → 1.22.0__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 (32) hide show
  1. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/PKG-INFO +25 -4
  2. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/README.md +19 -2
  3. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/PKG-INFO +25 -4
  4. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/SOURCES.txt +1 -0
  5. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/requires.txt +6 -1
  6. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/asyncpg.py +3 -1
  7. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/client.py +61 -19
  8. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connection_info.py +15 -5
  9. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connection_name.py +2 -4
  10. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connector.py +95 -62
  11. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/enums.py +2 -1
  12. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/exceptions.py +6 -6
  13. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/instance.py +1 -1
  14. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/lazy.py +5 -5
  15. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/monitored_cache.py +42 -11
  16. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pg8000.py +3 -1
  17. cloud_sql_python_connector-1.22.0/google/cloud/sql/connector/psycopg.py +235 -0
  18. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pymysql.py +8 -1
  19. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pytds.py +1 -1
  20. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/rate_limiter.py +3 -3
  21. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/refresh_utils.py +4 -2
  22. cloud_sql_python_connector-1.22.0/google/cloud/sql/connector/resolver.py +173 -0
  23. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/utils.py +40 -8
  24. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/version.py +1 -1
  25. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/pyproject.toml +4 -1
  26. cloud_sql_python_connector-1.20.4/google/cloud/sql/connector/resolver.py +0 -91
  27. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/LICENSE +0 -0
  28. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/dependency_links.txt +0 -0
  29. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/top_level.txt +0 -0
  30. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/__init__.py +2 -2
  31. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/py.typed +0 -0
  32. {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloud-sql-python-connector
3
- Version: 1.20.4
3
+ Version: 1.22.0
4
4
  Summary: Google Cloud SQL Python Connector library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License-Expression: Apache-2.0
@@ -21,12 +21,14 @@ Classifier: Operating System :: OS Independent
21
21
  Requires-Python: >=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
- Requires-Dist: aiofiles
25
24
  Requires-Dist: aiohttp
26
25
  Requires-Dist: cryptography>=42.0.0
27
26
  Requires-Dist: dnspython>=2.0.0
28
27
  Requires-Dist: Requests
29
28
  Requires-Dist: google-auth>=2.28.0
29
+ Requires-Dist: grpcio
30
+ Requires-Dist: protobuf
31
+ Requires-Dist: googleapis-common-protos
30
32
  Provides-Extra: pymysql
31
33
  Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
32
34
  Provides-Extra: pg8000
@@ -35,6 +37,8 @@ Provides-Extra: pytds
35
37
  Requires-Dist: python-tds>=1.15.0; extra == "pytds"
36
38
  Provides-Extra: asyncpg
37
39
  Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
40
+ Provides-Extra: psycopg
41
+ Requires-Dist: psycopg>=3.1.0; extra == "psycopg"
38
42
  Dynamic: license-file
39
43
 
40
44
  <p align="center">
@@ -80,7 +84,8 @@ providing the following benefits:
80
84
  The Cloud SQL Python Connector is a package to be used alongside a database driver.
81
85
  Currently supported drivers are:
82
86
  - [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
83
- - [`pg8000`](https://github.com/tlocke/pg8000) (PostgreSQL)
87
+ - [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
88
+ - [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
84
89
  - [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
85
90
  - [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
86
91
 
@@ -95,12 +100,16 @@ based on your database dialect.
95
100
  pip install "cloud-sql-python-connector[pymysql]"
96
101
  ```
97
102
  ### Postgres
98
- There are two different database drivers that are supported for the Postgres dialect:
103
+ There are three different database drivers that are supported for the Postgres dialect:
99
104
 
100
105
  #### pg8000
101
106
  ```
102
107
  pip install "cloud-sql-python-connector[pg8000]"
103
108
  ```
109
+ #### psycopg
110
+ ```
111
+ pip install "cloud-sql-python-connector[psycopg]"
112
+ ```
104
113
  #### asyncpg
105
114
  ```
106
115
  pip install "cloud-sql-python-connector[asyncpg]"
@@ -176,6 +185,18 @@ pool = sqlalchemy.create_engine(
176
185
  db="my-db-name"
177
186
  ),
178
187
  )
188
+
189
+ # Or with Postgres (psycopg):
190
+ pool = sqlalchemy.create_engine(
191
+ "postgresql+psycopg://",
192
+ creator=lambda: connector.connect(
193
+ "project:region:instance",
194
+ "psycopg",
195
+ user="my-user",
196
+ password="my-password",
197
+ db="my-db-name"
198
+ ),
199
+ )
179
200
  ```
180
201
 
181
202
  The returned connection pool engine can then be used to query and modify the database.
@@ -41,7 +41,8 @@ providing the following benefits:
41
41
  The Cloud SQL Python Connector is a package to be used alongside a database driver.
42
42
  Currently supported drivers are:
43
43
  - [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
44
- - [`pg8000`](https://github.com/tlocke/pg8000) (PostgreSQL)
44
+ - [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
45
+ - [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
45
46
  - [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
46
47
  - [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
47
48
 
@@ -56,12 +57,16 @@ based on your database dialect.
56
57
  pip install "cloud-sql-python-connector[pymysql]"
57
58
  ```
58
59
  ### Postgres
59
- There are two different database drivers that are supported for the Postgres dialect:
60
+ There are three different database drivers that are supported for the Postgres dialect:
60
61
 
61
62
  #### pg8000
62
63
  ```
63
64
  pip install "cloud-sql-python-connector[pg8000]"
64
65
  ```
66
+ #### psycopg
67
+ ```
68
+ pip install "cloud-sql-python-connector[psycopg]"
69
+ ```
65
70
  #### asyncpg
66
71
  ```
67
72
  pip install "cloud-sql-python-connector[asyncpg]"
@@ -137,6 +142,18 @@ pool = sqlalchemy.create_engine(
137
142
  db="my-db-name"
138
143
  ),
139
144
  )
145
+
146
+ # Or with Postgres (psycopg):
147
+ pool = sqlalchemy.create_engine(
148
+ "postgresql+psycopg://",
149
+ creator=lambda: connector.connect(
150
+ "project:region:instance",
151
+ "psycopg",
152
+ user="my-user",
153
+ password="my-password",
154
+ db="my-db-name"
155
+ ),
156
+ )
140
157
  ```
141
158
 
142
159
  The returned connection pool engine can then be used to query and modify the database.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloud-sql-python-connector
3
- Version: 1.20.4
3
+ Version: 1.22.0
4
4
  Summary: Google Cloud SQL Python Connector library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License-Expression: Apache-2.0
@@ -21,12 +21,14 @@ Classifier: Operating System :: OS Independent
21
21
  Requires-Python: >=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
- Requires-Dist: aiofiles
25
24
  Requires-Dist: aiohttp
26
25
  Requires-Dist: cryptography>=42.0.0
27
26
  Requires-Dist: dnspython>=2.0.0
28
27
  Requires-Dist: Requests
29
28
  Requires-Dist: google-auth>=2.28.0
29
+ Requires-Dist: grpcio
30
+ Requires-Dist: protobuf
31
+ Requires-Dist: googleapis-common-protos
30
32
  Provides-Extra: pymysql
31
33
  Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
32
34
  Provides-Extra: pg8000
@@ -35,6 +37,8 @@ Provides-Extra: pytds
35
37
  Requires-Dist: python-tds>=1.15.0; extra == "pytds"
36
38
  Provides-Extra: asyncpg
37
39
  Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
40
+ Provides-Extra: psycopg
41
+ Requires-Dist: psycopg>=3.1.0; extra == "psycopg"
38
42
  Dynamic: license-file
39
43
 
40
44
  <p align="center">
@@ -80,7 +84,8 @@ providing the following benefits:
80
84
  The Cloud SQL Python Connector is a package to be used alongside a database driver.
81
85
  Currently supported drivers are:
82
86
  - [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
83
- - [`pg8000`](https://github.com/tlocke/pg8000) (PostgreSQL)
87
+ - [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
88
+ - [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
84
89
  - [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
85
90
  - [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
86
91
 
@@ -95,12 +100,16 @@ based on your database dialect.
95
100
  pip install "cloud-sql-python-connector[pymysql]"
96
101
  ```
97
102
  ### Postgres
98
- There are two different database drivers that are supported for the Postgres dialect:
103
+ There are three different database drivers that are supported for the Postgres dialect:
99
104
 
100
105
  #### pg8000
101
106
  ```
102
107
  pip install "cloud-sql-python-connector[pg8000]"
103
108
  ```
109
+ #### psycopg
110
+ ```
111
+ pip install "cloud-sql-python-connector[psycopg]"
112
+ ```
104
113
  #### asyncpg
105
114
  ```
106
115
  pip install "cloud-sql-python-connector[asyncpg]"
@@ -176,6 +185,18 @@ pool = sqlalchemy.create_engine(
176
185
  db="my-db-name"
177
186
  ),
178
187
  )
188
+
189
+ # Or with Postgres (psycopg):
190
+ pool = sqlalchemy.create_engine(
191
+ "postgresql+psycopg://",
192
+ creator=lambda: connector.connect(
193
+ "project:region:instance",
194
+ "psycopg",
195
+ user="my-user",
196
+ password="my-password",
197
+ db="my-db-name"
198
+ ),
199
+ )
179
200
  ```
180
201
 
181
202
  The returned connection pool engine can then be used to query and modify the database.
@@ -18,6 +18,7 @@ google/cloud/sql/connector/instance.py
18
18
  google/cloud/sql/connector/lazy.py
19
19
  google/cloud/sql/connector/monitored_cache.py
20
20
  google/cloud/sql/connector/pg8000.py
21
+ google/cloud/sql/connector/psycopg.py
21
22
  google/cloud/sql/connector/py.typed
22
23
  google/cloud/sql/connector/pymysql.py
23
24
  google/cloud/sql/connector/pytds.py
@@ -1,9 +1,11 @@
1
- aiofiles
2
1
  aiohttp
3
2
  cryptography>=42.0.0
4
3
  dnspython>=2.0.0
5
4
  Requests
6
5
  google-auth>=2.28.0
6
+ grpcio
7
+ protobuf
8
+ googleapis-common-protos
7
9
 
8
10
  [asyncpg]
9
11
  asyncpg>=0.30.0
@@ -11,6 +13,9 @@ asyncpg>=0.30.0
11
13
  [pg8000]
12
14
  pg8000>=1.31.1
13
15
 
16
+ [psycopg]
17
+ psycopg>=3.1.0
18
+
14
19
  [pymysql]
15
20
  PyMySQL>=1.1.0
16
21
 
@@ -51,7 +51,9 @@ async def connect(
51
51
  'Unable to import module "asyncpg." Please install and try again.'
52
52
  )
53
53
  user = kwargs.pop("user")
54
- db = kwargs.pop("db")
54
+ db = kwargs.pop("database", kwargs.pop("db", None))
55
+ if db is None:
56
+ raise KeyError("database")
55
57
  passwd = kwargs.pop("password", None)
56
58
 
57
59
  return await asyncpg.connect(
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  import asyncio
18
18
  import datetime
19
19
  import logging
20
- from typing import Any, Optional, TYPE_CHECKING
20
+ from typing import Any, TYPE_CHECKING
21
21
 
22
22
  import aiohttp
23
23
  from cryptography.hazmat.backends import default_backend
@@ -42,7 +42,7 @@ DEFAULT_SERVICE_ENDPOINT: str = "https://sqladmin.googleapis.com"
42
42
  logger = logging.getLogger(name=__name__)
43
43
 
44
44
 
45
- def _format_user_agent(driver: Optional[str], custom: Optional[str]) -> str:
45
+ def _format_user_agent(driver: str | None, custom: str | None) -> str:
46
46
  agent = f"{USER_AGENT}+{driver}" if driver else USER_AGENT
47
47
  if custom and isinstance(custom, str):
48
48
  agent = f"{agent} {custom}"
@@ -52,12 +52,12 @@ def _format_user_agent(driver: Optional[str], custom: Optional[str]) -> str:
52
52
  class CloudSQLClient:
53
53
  def __init__(
54
54
  self,
55
- sqladmin_api_endpoint: Optional[str],
56
- quota_project: Optional[str],
55
+ sqladmin_api_endpoint: str | None,
56
+ quota_project: str | None,
57
57
  credentials: Credentials,
58
- client: Optional[aiohttp.ClientSession] = None,
59
- driver: Optional[str] = None,
60
- user_agent: Optional[str] = None,
58
+ client: aiohttp.ClientSession | None = None,
59
+ driver: str | None = None,
60
+ user_agent: str | None = None,
61
61
  ) -> None:
62
62
  """Establishes the client to be used for Cloud SQL Admin API requests.
63
63
 
@@ -135,7 +135,7 @@ class CloudSQLClient:
135
135
  if message:
136
136
  resp.reason = message
137
137
  # skip, raise_for_status will catch all errors in finally block
138
- except Exception:
138
+ except Exception: # noqa: BLE001, S110
139
139
  pass
140
140
  finally:
141
141
  resp.raise_for_status()
@@ -146,7 +146,7 @@ class CloudSQLClient:
146
146
  )
147
147
 
148
148
  ip_addresses = (
149
- {ip["type"]: ip["ipAddress"] for ip in ret_dict["ipAddresses"]}
149
+ {ip["type"]: [ip["ipAddress"]] for ip in ret_dict["ipAddresses"]}
150
150
  if "ipAddresses" in ret_dict
151
151
  else {}
152
152
  )
@@ -156,20 +156,22 @@ class CloudSQLClient:
156
156
  if ret_dict.get("pscEnabled"):
157
157
  # Find PSC instance DNS name in the dns_names field
158
158
  psc_dns_names = [
159
- d["name"]
159
+ d["name"].rstrip(".")
160
160
  for d in ret_dict.get("dnsNames", [])
161
161
  if d["connectionType"] == "PRIVATE_SERVICE_CONNECT"
162
162
  and d["dnsScope"] == "INSTANCE"
163
163
  ]
164
- dns_name = psc_dns_names[0] if psc_dns_names else None
164
+ # Sort: .sql-psc.goog first
165
+ psc_dns_names.sort(key=lambda x: x.endswith(".sql-psc.goog"), reverse=True)
165
166
 
166
167
  # Fall back do dns_name field if dns_names is not set
167
- if dns_name is None:
168
+ if not psc_dns_names:
168
169
  dns_name = ret_dict.get("dnsName", None)
170
+ if dns_name:
171
+ psc_dns_names = [dns_name.rstrip(".")]
169
172
 
170
- # Remove trailing period from DNS name. Required for SSL in Python
171
- if dns_name:
172
- ip_addresses["PSC"] = dns_name.rstrip(".")
173
+ if psc_dns_names:
174
+ ip_addresses["PSC"] = psc_dns_names
173
175
 
174
176
  return {
175
177
  "ip_addresses": ip_addresses,
@@ -177,6 +179,47 @@ class CloudSQLClient:
177
179
  "database_version": ret_dict["databaseVersion"],
178
180
  }
179
181
 
182
+ async def resolve_connect_settings(
183
+ self,
184
+ dns_name: str,
185
+ location: str,
186
+ ) -> dict[str, Any]:
187
+ """Asynchronously calls the resolveConnectSettings endpoint to resolve a
188
+ PSC DNS name to a connection name.
189
+
190
+ Args:
191
+ dns_name (str): The DNS name of the Cloud SQL instance.
192
+ location (str): The region/location of the instance.
193
+
194
+ Returns:
195
+ A dictionary containing the resolve response (e.g. connectionName).
196
+ """
197
+ # before making Cloud SQL Admin API calls, refresh creds if required
198
+ if self._credentials.token_state != TokenState.FRESH:
199
+ self._credentials.refresh(requests.Request())
200
+
201
+ headers = {
202
+ "Authorization": f"Bearer {self._credentials.token}",
203
+ }
204
+
205
+ url = f"{self._sqladmin_api_endpoint}/sql/{API_VERSION}/locations/{location}/dns/{dns_name}:resolveConnectSettings"
206
+
207
+ resp = await self._client.get(url, headers=headers)
208
+ if resp.status >= 500:
209
+ resp = await retry_50x(self._client.get, url, headers=headers)
210
+ try:
211
+ ret_dict = await resp.json()
212
+ if resp.status >= 400:
213
+ message = ret_dict.get("error", {}).get("message")
214
+ if message:
215
+ resp.reason = message
216
+ except Exception: # noqa: BLE001, S110
217
+ pass
218
+ finally:
219
+ resp.raise_for_status()
220
+
221
+ return ret_dict
222
+
180
223
  async def _get_ephemeral(
181
224
  self,
182
225
  project: str,
@@ -223,7 +266,7 @@ class CloudSQLClient:
223
266
  if message:
224
267
  resp.reason = message
225
268
  # skip, raise_for_status will catch all errors in finally block
226
- except Exception:
269
+ except Exception: # noqa: BLE001, S110
227
270
  pass
228
271
  finally:
229
272
  resp.raise_for_status()
@@ -244,8 +287,7 @@ class CloudSQLClient:
244
287
  # Ref: https://github.com/googleapis/google-auth-library-python/blob/49a5ff7411a2ae4d32a7d11700f9f961c55406a9/google/auth/_helpers.py#L93-L99
245
288
  token_expiration = token_expiration.replace(tzinfo=datetime.timezone.utc)
246
289
 
247
- if expiration > token_expiration:
248
- expiration = token_expiration
290
+ expiration = min(expiration, token_expiration)
249
291
  return ephemeral_cert, expiration
250
292
 
251
293
  async def get_connection_info(
@@ -274,7 +316,7 @@ class CloudSQLClient:
274
316
  """
275
317
  priv_key, pub_key = await keys
276
318
  # before making Cloud SQL Admin API calls, refresh creds if required
277
- if not self._credentials.token_state == TokenState.FRESH:
319
+ if self._credentials.token_state != TokenState.FRESH:
278
320
  self._credentials.refresh(requests.Request())
279
321
 
280
322
  metadata_task = asyncio.create_task(
@@ -18,13 +18,12 @@ import abc
18
18
  from dataclasses import dataclass
19
19
  import logging
20
20
  import ssl
21
- from typing import Any, Optional, TYPE_CHECKING
22
-
23
- from aiofiles.tempfile import TemporaryDirectory
21
+ from typing import Any, TYPE_CHECKING
24
22
 
25
23
  from google.cloud.sql.connector.connection_name import ConnectionName
26
24
  from google.cloud.sql.connector.exceptions import CloudSQLIPTypeError
27
25
  from google.cloud.sql.connector.exceptions import TLSVersionError
26
+ from google.cloud.sql.connector.utils import AsyncTemporaryDirectory
28
27
  from google.cloud.sql.connector.utils import write_to_file
29
28
 
30
29
  if TYPE_CHECKING:
@@ -68,7 +67,7 @@ class ConnectionInfo:
68
67
  ip_addrs: dict[str, Any]
69
68
  database_version: str
70
69
  expiration: datetime.datetime
71
- context: Optional[ssl.SSLContext] = None
70
+ context: ssl.SSLContext | None = None
72
71
 
73
72
  async def create_ssl_context(self, enable_iam_auth: bool = False) -> ssl.SSLContext:
74
73
  """Constructs a SSL/TLS context for the given connection info.
@@ -108,7 +107,7 @@ class ConnectionInfo:
108
107
  # tmpdir and its contents are automatically deleted after the CA cert
109
108
  # and ephemeral cert are loaded into the SSLcontext. The values
110
109
  # need to be written to files in order to be loaded by the SSLContext
111
- async with TemporaryDirectory() as tmpdir:
110
+ async with AsyncTemporaryDirectory() as tmpdir:
112
111
  ca_filename, cert_filename, key_filename = await write_to_file(
113
112
  tmpdir, self.server_ca_cert, self.client_cert, self.private_key
114
113
  )
@@ -122,6 +121,17 @@ class ConnectionInfo:
122
121
  """Returns the first IP address for the instance, according to the preference
123
122
  supplied by ip_type. If no IP addressess with the given preference are found,
124
123
  an error is raised."""
124
+ if ip_type.value in self.ip_addrs:
125
+ return self.ip_addrs[ip_type.value][0]
126
+ raise CloudSQLIPTypeError(
127
+ "Cloud SQL instance does not have any IP addresses matching "
128
+ f"preference: {ip_type.value}"
129
+ )
130
+
131
+ def get_preferred_ips(self, ip_type: IPTypes) -> list[str]:
132
+ """Returns all IP addresses for the instance, according to the preference
133
+ supplied by ip_type. If no IP addressess with the given preference are found,
134
+ an error is raised."""
125
135
  if ip_type.value in self.ip_addrs:
126
136
  return self.ip_addrs[ip_type.value]
127
137
  raise CloudSQLIPTypeError(
@@ -18,7 +18,7 @@ import re
18
18
  # Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE_NAME>
19
19
  # Additionally, we have to support legacy "domain-scoped" projects
20
20
  # (e.g. "google.com:PROJECT")
21
- CONN_NAME_REGEX = re.compile(("([^:]+(:[^:]+)?):([^:]+):([^:]+)"))
21
+ CONN_NAME_REGEX = re.compile("([^:]+(:[^:]+)?):([^:]+):([^:]+)")
22
22
  # The domain name pattern in accordance with RFC 1035, RFC 1123 and RFC 2181.
23
23
  DOMAIN_NAME_REGEX = re.compile(
24
24
  r"^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$"
@@ -48,9 +48,7 @@ class ConnectionName:
48
48
 
49
49
 
50
50
  def _is_valid_domain(domain_name: str) -> bool:
51
- if DOMAIN_NAME_REGEX.fullmatch(domain_name) is None:
52
- return False
53
- return True
51
+ return DOMAIN_NAME_REGEX.fullmatch(domain_name) is not None
54
52
 
55
53
 
56
54
  def _parse_connection_name(connection_name: str) -> ConnectionName: