|
10 | 10 | from datetime import datetime |
11 | 11 | from multiprocessing import cpu_count |
12 | 12 | from re import Pattern |
13 | | -from typing import TYPE_CHECKING, Any, cast |
| 13 | +from typing import Any, cast |
14 | 14 |
|
15 | 15 | import botocore.exceptions |
16 | 16 | from boto3 import Session |
|
22 | 22 | from fsspec.utils import tokenize |
23 | 23 |
|
24 | 24 | import pyathena |
| 25 | +from pyathena.connection import Connection |
25 | 26 | from pyathena.filesystem.s3_errors import S3ClientError |
26 | 27 | from pyathena.filesystem.s3_executor import S3Executor, S3ThreadPoolExecutor |
27 | 28 | from pyathena.filesystem.s3_object import ( |
|
37 | 38 | ) |
38 | 39 | from pyathena.util import RetryConfig, retry_api_call |
39 | 40 |
|
40 | | -if TYPE_CHECKING: |
41 | | - from pyathena.connection import Connection |
42 | | - |
43 | 41 | _logger = logging.getLogger(__name__) |
44 | 42 |
|
45 | 43 |
|
@@ -173,55 +171,59 @@ def __init__( |
173 | 171 | self.request_kwargs = {"RequestPayer": "requester"} if requester_pays else {} |
174 | 172 |
|
175 | 173 | def _get_client_compatible_with_s3fs(self, **kwargs) -> BaseClient: |
176 | | - """ |
177 | | - https://github.com/fsspec/s3fs/blob/2023.4.0/s3fs/core.py#L457-L535 |
178 | | - """ |
179 | | - from pyathena.connection import Connection |
| 174 | + """Build a boto3 S3 client from s3fs-compatible constructor arguments. |
| 175 | +
|
| 176 | + Accepts the constructor arguments that s3fs users pass through fsspec |
| 177 | + storage options — ``key``/``username``, ``secret``/``password``, |
| 178 | + ``token``, ``anon``, ``use_ssl``, ``endpoint_url``, |
| 179 | + ``connect_timeout``/``read_timeout``, and the ``client_kwargs`` / |
| 180 | + ``config_kwargs`` dictionaries — in addition to boto3 session |
| 181 | + arguments such as ``region_name`` and ``profile_name``. |
180 | 182 |
|
| 183 | + Args: |
| 184 | + **kwargs: The filesystem constructor arguments. |
| 185 | +
|
| 186 | + Returns: |
| 187 | + A boto3 S3 client configured from the arguments. |
| 188 | + """ |
181 | 189 | config_kwargs = deepcopy(kwargs.pop("config_kwargs", {})) |
| 190 | + client_kwargs = deepcopy(kwargs.pop("client_kwargs", {})) |
| 191 | + |
182 | 192 | user_agent_extra = config_kwargs.pop("user_agent_extra", None) |
183 | | - if user_agent_extra: |
184 | | - if pyathena.user_agent_extra not in user_agent_extra: |
185 | | - config_kwargs.update( |
186 | | - {"user_agent_extra": f"{pyathena.user_agent_extra} {user_agent_extra}"} |
187 | | - ) |
188 | | - else: |
189 | | - config_kwargs.update({"user_agent_extra": user_agent_extra}) |
190 | | - else: |
191 | | - config_kwargs.update({"user_agent_extra": pyathena.user_agent_extra}) |
192 | | - connect_timeout = kwargs.pop("connect_timeout", None) |
193 | | - if connect_timeout: |
| 193 | + if user_agent_extra and pyathena.user_agent_extra not in user_agent_extra: |
| 194 | + user_agent_extra = f"{pyathena.user_agent_extra} {user_agent_extra}" |
| 195 | + config_kwargs.update({"user_agent_extra": user_agent_extra or pyathena.user_agent_extra}) |
| 196 | + if connect_timeout := kwargs.pop("connect_timeout", None): |
194 | 197 | config_kwargs.update({"connect_timeout": connect_timeout}) |
195 | | - read_timeout = kwargs.pop("read_timeout", None) |
196 | | - if read_timeout: |
| 198 | + if read_timeout := kwargs.pop("read_timeout", None): |
197 | 199 | config_kwargs.update({"read_timeout": read_timeout}) |
198 | 200 |
|
199 | | - client_kwargs = deepcopy(kwargs.pop("client_kwargs", {})) |
200 | 201 | use_ssl = kwargs.pop("use_ssl", None) |
201 | | - if use_ssl: |
| 202 | + if use_ssl is not None: |
202 | 203 | client_kwargs.update({"use_ssl": use_ssl}) |
203 | | - endpoint_url = kwargs.pop("endpoint_url", None) |
204 | | - if endpoint_url: |
| 204 | + if endpoint_url := kwargs.pop("endpoint_url", None): |
205 | 205 | client_kwargs.update({"endpoint_url": endpoint_url}) |
206 | | - anon = kwargs.pop("anon", False) |
207 | | - if anon: |
| 206 | + if kwargs.pop("anon", False): |
208 | 207 | config_kwargs.update({"signature_version": UNSIGNED}) |
209 | 208 | else: |
210 | 209 | creds = { |
211 | | - "aws_access_key_id": kwargs.pop("key", kwargs.pop("username", None)), |
212 | | - "aws_secret_access_key": kwargs.pop("secret", kwargs.pop("password", None)), |
213 | | - "aws_session_token": kwargs.pop("token", None), |
| 210 | + key: value |
| 211 | + for key, value in { |
| 212 | + "aws_access_key_id": kwargs.pop("key", kwargs.pop("username", None)), |
| 213 | + "aws_secret_access_key": kwargs.pop("secret", kwargs.pop("password", None)), |
| 214 | + "aws_session_token": kwargs.pop("token", None), |
| 215 | + }.items() |
| 216 | + if value is not None |
214 | 217 | } |
215 | | - kwargs.update(**creds) |
216 | | - client_kwargs.update(**creds) |
| 218 | + kwargs.update(creds) |
| 219 | + client_kwargs.update(creds) |
217 | 220 |
|
218 | | - config = Config(**config_kwargs) |
219 | 221 | session = Session( |
220 | 222 | **{k: v for k, v in kwargs.items() if k in Connection._SESSION_PASSING_ARGS} |
221 | 223 | ) |
222 | 224 | return session.client( |
223 | 225 | "s3", |
224 | | - config=config, |
| 226 | + config=Config(**config_kwargs), |
225 | 227 | **{k: v for k, v in client_kwargs.items() if k in Connection._CLIENT_PASSING_ARGS}, |
226 | 228 | ) |
227 | 229 |
|
|
0 commit comments