PyAthena 3.30.0__tar.gz → 3.31.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.
- {pyathena-3.30.0 → pyathena-3.31.0}/PKG-INFO +8 -8
- {pyathena-3.30.0 → pyathena-3.31.0}/README.md +5 -5
- pyathena-3.31.0/pyathena/_version.py +24 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/converter.py +2 -1
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/converter.py +25 -3
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/model.py +50 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/converter.py +4 -2
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/result_set.py +19 -57
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/parser.py +0 -23
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/converter.py +2 -1
- {pyathena-3.30.0 → pyathena-3.31.0}/pyproject.toml +5 -5
- pyathena-3.30.0/pyathena/_version.py +0 -34
- {pyathena-3.30.0 → pyathena-3.31.0}/.gitignore +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/LICENSE +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/arrow/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/arrow/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/common.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/connection.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/pandas/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/pandas/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/polars/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/polars/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/result_set.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/s3fs/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/s3fs/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/spark/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/spark/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/arrow.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/base.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/pandas.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/polars.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/rest.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/sqlalchemy/s3fs.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/aio/util.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/result_set.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/arrow/util.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/common.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/connection.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/error.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/filesystem/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/filesystem/s3.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/filesystem/s3_async.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/filesystem/s3_executor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/filesystem/s3_object.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/formatter.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/pandas/util.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/result_set.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/polars/util.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/py.typed +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/result_set.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/converter.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/reader.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/s3fs/result_set.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/spark/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/spark/async_cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/spark/common.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/spark/cursor.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/__init__.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/arrow.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/base.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/compiler.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/constants.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/pandas.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/polars.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/preparer.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/requirements.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/rest.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/s3fs.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/types.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/sqlalchemy/util.py +0 -0
- {pyathena-3.30.0 → pyathena-3.31.0}/pyathena/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyAthena
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.31.0
|
|
4
4
|
Summary: Python DB API 2.0 (PEP 249) client for Amazon Athena
|
|
5
5
|
Project-URL: homepage, https://github.com/pyathena-dev/PyAthena/
|
|
6
6
|
Project-URL: repository, https://github.com/pyathena-dev/PyAthena/
|
|
@@ -38,8 +38,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
38
38
|
Classifier: Programming Language :: Python :: 3.14
|
|
39
39
|
Classifier: Topic :: Database :: Front-Ends
|
|
40
40
|
Requires-Python: >=3.10
|
|
41
|
-
Requires-Dist: boto3>=1.
|
|
42
|
-
Requires-Dist: botocore>=1.
|
|
41
|
+
Requires-Dist: boto3>=1.38.2
|
|
42
|
+
Requires-Dist: botocore>=1.41.2
|
|
43
43
|
Requires-Dist: fsspec
|
|
44
44
|
Requires-Dist: python-dateutil
|
|
45
45
|
Requires-Dist: tenacity>=4.1.0
|
|
@@ -80,7 +80,7 @@ PyAthena is a Python [DB API 2.0 (PEP 249)](https://www.python.org/dev/peps/pep-
|
|
|
80
80
|
|
|
81
81
|
## Requirements
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
- Python
|
|
84
84
|
|
|
85
85
|
- CPython 3.10, 3.11, 3.12, 3.13, 3.14
|
|
86
86
|
|
|
@@ -136,10 +136,10 @@ Many of the implementations in this library are based on [PyHive](https://github
|
|
|
136
136
|
|
|
137
137
|
## Links
|
|
138
138
|
|
|
139
|
-
- Documentation: https://pyathena.dev
|
|
140
|
-
- PyPI Releases: https://pypi.org/project/PyAthena
|
|
141
|
-
- Source Code: https://github.com/pyathena-dev/PyAthena
|
|
142
|
-
- Issue Tracker: https://github.com/pyathena-dev/PyAthena/issues
|
|
139
|
+
- Documentation: <https://pyathena.dev/>
|
|
140
|
+
- PyPI Releases: <https://pypi.org/project/PyAthena/>
|
|
141
|
+
- Source Code: <https://github.com/pyathena-dev/PyAthena/>
|
|
142
|
+
- Issue Tracker: <https://github.com/pyathena-dev/PyAthena/issues>
|
|
143
143
|
|
|
144
144
|
## Logo
|
|
145
145
|
|
|
@@ -21,7 +21,7 @@ PyAthena is a Python [DB API 2.0 (PEP 249)](https://www.python.org/dev/peps/pep-
|
|
|
21
21
|
|
|
22
22
|
## Requirements
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
- Python
|
|
25
25
|
|
|
26
26
|
- CPython 3.10, 3.11, 3.12, 3.13, 3.14
|
|
27
27
|
|
|
@@ -77,10 +77,10 @@ Many of the implementations in this library are based on [PyHive](https://github
|
|
|
77
77
|
|
|
78
78
|
## Links
|
|
79
79
|
|
|
80
|
-
- Documentation: https://pyathena.dev
|
|
81
|
-
- PyPI Releases: https://pypi.org/project/PyAthena
|
|
82
|
-
- Source Code: https://github.com/pyathena-dev/PyAthena
|
|
83
|
-
- Issue Tracker: https://github.com/pyathena-dev/PyAthena/issues
|
|
80
|
+
- Documentation: <https://pyathena.dev/>
|
|
81
|
+
- PyPI Releases: <https://pypi.org/project/PyAthena/>
|
|
82
|
+
- Source Code: <https://github.com/pyathena-dev/PyAthena/>
|
|
83
|
+
- Issue Tracker: <https://github.com/pyathena-dev/PyAthena/issues>
|
|
84
84
|
|
|
85
85
|
## Logo
|
|
86
86
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '3.31.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 31, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -3,12 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import binascii
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
+
import re
|
|
6
7
|
from abc import ABCMeta, abstractmethod
|
|
7
8
|
from collections.abc import Callable
|
|
8
9
|
from copy import deepcopy
|
|
9
10
|
from datetime import date, datetime, time
|
|
10
11
|
from decimal import Decimal
|
|
11
|
-
from typing import Any
|
|
12
|
+
from typing import Any, ClassVar
|
|
12
13
|
|
|
13
14
|
from dateutil.tz import gettz
|
|
14
15
|
|
|
@@ -16,7 +17,6 @@ from pyathena.parser import (
|
|
|
16
17
|
TypedValueConverter,
|
|
17
18
|
TypeNode,
|
|
18
19
|
TypeSignatureParser,
|
|
19
|
-
_normalize_hive_syntax,
|
|
20
20
|
_split_array_items,
|
|
21
21
|
)
|
|
22
22
|
from pyathena.util import strtobool
|
|
@@ -551,6 +551,9 @@ class DefaultTypeConverter(Converter):
|
|
|
551
551
|
['1', '2', '3']
|
|
552
552
|
"""
|
|
553
553
|
|
|
554
|
+
_HIVE_SYNTAX_RE: ClassVar[re.Pattern[str]] = re.compile(r"[<>:]")
|
|
555
|
+
_HIVE_REPLACEMENTS: ClassVar[dict[str, str]] = {"<": "(", ">": ")", ":": " "}
|
|
556
|
+
|
|
554
557
|
def __init__(self) -> None:
|
|
555
558
|
super().__init__(mappings=deepcopy(_DEFAULT_CONVERTERS), default=_to_default)
|
|
556
559
|
self._parser = TypeSignatureParser()
|
|
@@ -561,6 +564,25 @@ class DefaultTypeConverter(Converter):
|
|
|
561
564
|
)
|
|
562
565
|
self._parsed_hints: dict[str, TypeNode] = {}
|
|
563
566
|
|
|
567
|
+
@staticmethod
|
|
568
|
+
def _normalize_hive_syntax(type_str: str) -> str:
|
|
569
|
+
"""Normalize Hive-style DDL syntax to Trino-style.
|
|
570
|
+
|
|
571
|
+
Converts angle-bracket notation (``array<struct<a:int>>``) to
|
|
572
|
+
parenthesized notation (``array(struct(a int))``).
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
type_str: Type signature string, possibly using Hive syntax.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
Normalized type signature using Trino-style parenthesized notation.
|
|
579
|
+
"""
|
|
580
|
+
if "<" not in type_str:
|
|
581
|
+
return type_str
|
|
582
|
+
return DefaultTypeConverter._HIVE_SYNTAX_RE.sub(
|
|
583
|
+
lambda m: DefaultTypeConverter._HIVE_REPLACEMENTS[m.group()], type_str
|
|
584
|
+
)
|
|
585
|
+
|
|
564
586
|
def convert(self, type_: str, value: str | None, type_hint: str | None = None) -> Any | None:
|
|
565
587
|
"""Convert a string value to the appropriate Python type.
|
|
566
588
|
|
|
@@ -605,7 +627,7 @@ class DefaultTypeConverter(Converter):
|
|
|
605
627
|
Returns:
|
|
606
628
|
Parsed TypeNode.
|
|
607
629
|
"""
|
|
608
|
-
normalized = _normalize_hive_syntax(type_hint)
|
|
630
|
+
normalized = self._normalize_hive_syntax(type_hint)
|
|
609
631
|
if normalized not in self._parsed_hints:
|
|
610
632
|
self._parsed_hints[normalized] = self._parser.parse(normalized)
|
|
611
633
|
return self._parsed_hints[normalized]
|
|
@@ -111,9 +111,13 @@ class AthenaQueryExecution:
|
|
|
111
111
|
self._query_planning_time_in_millis: int | None = statistics.get(
|
|
112
112
|
"QueryPlanningTimeInMillis", None
|
|
113
113
|
)
|
|
114
|
+
self._service_pre_processing_time_in_millis: int | None = statistics.get(
|
|
115
|
+
"ServicePreProcessingTimeInMillis", None
|
|
116
|
+
)
|
|
114
117
|
self._service_processing_time_in_millis: int | None = statistics.get(
|
|
115
118
|
"ServiceProcessingTimeInMillis", None
|
|
116
119
|
)
|
|
120
|
+
self._dpu_count: float | None = statistics.get("DpuCount")
|
|
117
121
|
self._data_manifest_location: str | None = statistics.get("DataManifestLocation")
|
|
118
122
|
reuse_info = statistics.get("ResultReuseInformation", {})
|
|
119
123
|
self._reused_previous_result: bool | None = reuse_info.get("ReusedPreviousResult")
|
|
@@ -127,6 +131,24 @@ class AthenaQueryExecution:
|
|
|
127
131
|
acl_conf = result_conf.get("AclConfiguration", {})
|
|
128
132
|
self._s3_acl_option: str | None = acl_conf.get("S3AclOption")
|
|
129
133
|
|
|
134
|
+
managed_results_conf = query_execution.get("ManagedQueryResultsConfiguration", {})
|
|
135
|
+
self._managed_query_results_enabled: bool | None = managed_results_conf.get("Enabled")
|
|
136
|
+
managed_results_encryption_conf = managed_results_conf.get("EncryptionConfiguration", {})
|
|
137
|
+
self._managed_query_results_kms_key: str | None = managed_results_encryption_conf.get(
|
|
138
|
+
"KmsKey"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
s3_access_grants_conf = query_execution.get("QueryResultsS3AccessGrantsConfiguration", {})
|
|
142
|
+
self._enable_s3_access_grants: bool | None = s3_access_grants_conf.get(
|
|
143
|
+
"EnableS3AccessGrants"
|
|
144
|
+
)
|
|
145
|
+
self._create_user_level_prefix: bool | None = s3_access_grants_conf.get(
|
|
146
|
+
"CreateUserLevelPrefix"
|
|
147
|
+
)
|
|
148
|
+
self._s3_access_grants_authentication_type: str | None = s3_access_grants_conf.get(
|
|
149
|
+
"AuthenticationType"
|
|
150
|
+
)
|
|
151
|
+
|
|
130
152
|
engine_version = query_execution.get("EngineVersion", {})
|
|
131
153
|
self._selected_engine_version: str | None = engine_version.get(
|
|
132
154
|
"SelectedEngineVersion", None
|
|
@@ -224,10 +246,18 @@ class AthenaQueryExecution:
|
|
|
224
246
|
def query_planning_time_in_millis(self) -> int | None:
|
|
225
247
|
return self._query_planning_time_in_millis
|
|
226
248
|
|
|
249
|
+
@property
|
|
250
|
+
def service_pre_processing_time_in_millis(self) -> int | None:
|
|
251
|
+
return self._service_pre_processing_time_in_millis
|
|
252
|
+
|
|
227
253
|
@property
|
|
228
254
|
def service_processing_time_in_millis(self) -> int | None:
|
|
229
255
|
return self._service_processing_time_in_millis
|
|
230
256
|
|
|
257
|
+
@property
|
|
258
|
+
def dpu_count(self) -> float | None:
|
|
259
|
+
return self._dpu_count
|
|
260
|
+
|
|
231
261
|
@property
|
|
232
262
|
def output_location(self) -> str | None:
|
|
233
263
|
return self._output_location
|
|
@@ -272,6 +302,26 @@ class AthenaQueryExecution:
|
|
|
272
302
|
def result_reuse_minutes(self) -> int | None:
|
|
273
303
|
return self._result_reuse_minutes
|
|
274
304
|
|
|
305
|
+
@property
|
|
306
|
+
def managed_query_results_enabled(self) -> bool | None:
|
|
307
|
+
return self._managed_query_results_enabled
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def managed_query_results_kms_key(self) -> str | None:
|
|
311
|
+
return self._managed_query_results_kms_key
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def enable_s3_access_grants(self) -> bool | None:
|
|
315
|
+
return self._enable_s3_access_grants
|
|
316
|
+
|
|
317
|
+
@property
|
|
318
|
+
def create_user_level_prefix(self) -> bool | None:
|
|
319
|
+
return self._create_user_level_prefix
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def s3_access_grants_authentication_type(self) -> str | None:
|
|
323
|
+
return self._s3_access_grants_authentication_type
|
|
324
|
+
|
|
275
325
|
|
|
276
326
|
class AthenaCalculationExecutionStatus:
|
|
277
327
|
"""Status information for an Athena calculation execution.
|
|
@@ -81,7 +81,8 @@ class DefaultPandasTypeConverter(Converter):
|
|
|
81
81
|
return self.__dtypes
|
|
82
82
|
|
|
83
83
|
def convert(self, type_: str, value: str | None, type_hint: str | None = None) -> Any | None:
|
|
84
|
-
|
|
84
|
+
converter = self.get(type_)
|
|
85
|
+
return converter(value)
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
class DefaultPandasUnloadTypeConverter(Converter):
|
|
@@ -104,4 +105,5 @@ class DefaultPandasUnloadTypeConverter(Converter):
|
|
|
104
105
|
)
|
|
105
106
|
|
|
106
107
|
def convert(self, type_: str, value: str | None, type_hint: str | None = None) -> Any | None:
|
|
107
|
-
|
|
108
|
+
converter = self.get(type_)
|
|
109
|
+
return converter(value)
|
|
@@ -320,38 +320,33 @@ class AthenaPandasResultSet(AthenaResultSet):
|
|
|
320
320
|
"""Determine the appropriate CSV engine based on configuration and compatibility.
|
|
321
321
|
|
|
322
322
|
Args:
|
|
323
|
-
file_size_bytes: Size of the CSV file in bytes.
|
|
323
|
+
file_size_bytes: Size of the CSV file in bytes. Only used for PyArrow
|
|
324
|
+
compatibility checks (minimum file size threshold).
|
|
324
325
|
chunksize: Chunksize parameter (overrides self._chunksize if provided).
|
|
325
326
|
|
|
326
327
|
Returns:
|
|
327
328
|
CSV engine name ('pyarrow', 'c', or 'python').
|
|
328
329
|
"""
|
|
329
|
-
|
|
330
|
+
if self._engine == "python":
|
|
331
|
+
return "python"
|
|
330
332
|
|
|
333
|
+
# Use PyArrow only when explicitly requested and all compatibility
|
|
334
|
+
# checks pass; otherwise fall through to the C engine default.
|
|
331
335
|
if self._engine == "pyarrow":
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return self._get_optimal_csv_engine(file_size_bytes)
|
|
345
|
-
|
|
346
|
-
# Check file size compatibility
|
|
347
|
-
if file_size_bytes is not None and file_size_bytes < self.PYARROW_MIN_FILE_SIZE_BYTES:
|
|
348
|
-
return self._get_optimal_csv_engine(file_size_bytes)
|
|
336
|
+
effective_chunksize = chunksize if chunksize is not None else self._chunksize
|
|
337
|
+
is_compatible = (
|
|
338
|
+
effective_chunksize is None
|
|
339
|
+
and self._quoting == 1
|
|
340
|
+
and not self.converters
|
|
341
|
+
and (file_size_bytes is None or file_size_bytes >= self.PYARROW_MIN_FILE_SIZE_BYTES)
|
|
342
|
+
)
|
|
343
|
+
if is_compatible:
|
|
344
|
+
try:
|
|
345
|
+
return self._get_available_engine(["pyarrow"])
|
|
346
|
+
except ImportError:
|
|
347
|
+
pass
|
|
349
348
|
|
|
350
|
-
|
|
351
|
-
try:
|
|
352
|
-
return self._get_available_engine(["pyarrow"])
|
|
353
|
-
except ImportError:
|
|
354
|
-
return self._get_optimal_csv_engine(file_size_bytes)
|
|
349
|
+
return "c"
|
|
355
350
|
|
|
356
351
|
def _get_available_engine(self, engine_candidates: list[str]) -> str:
|
|
357
352
|
"""Get the first available engine from a list of candidates.
|
|
@@ -382,19 +377,6 @@ class AthenaPandasResultSet(AthenaResultSet):
|
|
|
382
377
|
f"{error_msgs}"
|
|
383
378
|
)
|
|
384
379
|
|
|
385
|
-
def _get_optimal_csv_engine(self, file_size_bytes: int | None = None) -> str:
|
|
386
|
-
"""Get the optimal CSV engine based on file size.
|
|
387
|
-
|
|
388
|
-
Args:
|
|
389
|
-
file_size_bytes: Size of the CSV file in bytes.
|
|
390
|
-
|
|
391
|
-
Returns:
|
|
392
|
-
'python' for large files (>50MB) to avoid C parser limits, otherwise 'c'.
|
|
393
|
-
"""
|
|
394
|
-
if file_size_bytes and file_size_bytes > self.LARGE_FILE_THRESHOLD_BYTES:
|
|
395
|
-
return "python"
|
|
396
|
-
return "c"
|
|
397
|
-
|
|
398
380
|
def _auto_determine_chunksize(self, file_size_bytes: int) -> int | None:
|
|
399
381
|
"""Determine appropriate chunksize for large files based on file size.
|
|
400
382
|
|
|
@@ -594,26 +576,6 @@ class AthenaPandasResultSet(AthenaResultSet):
|
|
|
594
576
|
|
|
595
577
|
except Exception as e:
|
|
596
578
|
_logger.exception("Failed to read %s.", self.output_location)
|
|
597
|
-
error_msg = str(e).lower()
|
|
598
|
-
if any(
|
|
599
|
-
phrase in error_msg
|
|
600
|
-
for phrase in ["signed integer", "maximum", "overflow", "int32", "c parser"]
|
|
601
|
-
):
|
|
602
|
-
# Enhanced error message with specific recommendations
|
|
603
|
-
file_mb = (length or 0) // (1024 * 1024)
|
|
604
|
-
detailed_msg = (
|
|
605
|
-
f"Large dataset processing error ({file_mb}MB file): {e}. "
|
|
606
|
-
"This is likely due to pandas C parser limitations. "
|
|
607
|
-
"Recommended solutions:\n"
|
|
608
|
-
"1. Set chunksize: cursor = connection.cursor(PandasCursor, chunksize=50000)\n"
|
|
609
|
-
"2. Enable auto-optimization: "
|
|
610
|
-
"cursor = connection.cursor(PandasCursor, auto_optimize_chunksize=True)\n"
|
|
611
|
-
"3. Use PyArrow engine: "
|
|
612
|
-
"cursor = connection.cursor(PandasCursor, engine='pyarrow')\n"
|
|
613
|
-
"4. Use Python engine: "
|
|
614
|
-
"cursor = connection.cursor(PandasCursor, engine='python')"
|
|
615
|
-
)
|
|
616
|
-
raise OperationalError(detailed_msg) from e
|
|
617
579
|
raise OperationalError(*e.args) from e
|
|
618
580
|
|
|
619
581
|
def _read_parquet(self, engine) -> DataFrame:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import re
|
|
5
4
|
from collections.abc import Callable
|
|
6
5
|
from dataclasses import dataclass, field
|
|
7
6
|
from typing import Any
|
|
@@ -11,28 +10,6 @@ _TYPE_ALIASES: dict[str, str] = {
|
|
|
11
10
|
"int": "integer",
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
# Pattern for normalizing Hive-style type signatures to Trino-style.
|
|
15
|
-
# Matches angle brackets and colons used in Hive DDL (e.g., array<struct<a:int>>).
|
|
16
|
-
_HIVE_SYNTAX_RE: re.Pattern[str] = re.compile(r"[<>:]")
|
|
17
|
-
_HIVE_REPLACEMENTS: dict[str, str] = {"<": "(", ">": ")", ":": " "}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _normalize_hive_syntax(type_str: str) -> str:
|
|
21
|
-
"""Normalize Hive-style DDL syntax to Trino-style.
|
|
22
|
-
|
|
23
|
-
Converts angle-bracket notation (``array<struct<a:int>>``) to
|
|
24
|
-
parenthesized notation (``array(struct(a int))``).
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
type_str: Type signature string, possibly using Hive syntax.
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
Normalized type signature using Trino-style parenthesized notation.
|
|
31
|
-
"""
|
|
32
|
-
if "<" not in type_str:
|
|
33
|
-
return type_str
|
|
34
|
-
return _HIVE_SYNTAX_RE.sub(lambda m: _HIVE_REPLACEMENTS[m.group()], type_str)
|
|
35
|
-
|
|
36
13
|
|
|
37
14
|
def _split_array_items(inner: str) -> list[str]:
|
|
38
15
|
"""Split array items by comma, respecting brace and bracket groupings.
|
|
@@ -5,8 +5,8 @@ authors = [
|
|
|
5
5
|
{name = "laughingman7743", email = "laughingman7743@gmail.com"},
|
|
6
6
|
]
|
|
7
7
|
dependencies = [
|
|
8
|
-
"boto3>=1.
|
|
9
|
-
"botocore>=1.
|
|
8
|
+
"boto3>=1.38.2",
|
|
9
|
+
"botocore>=1.41.2",
|
|
10
10
|
"tenacity>=4.1.0",
|
|
11
11
|
"fsspec",
|
|
12
12
|
"python-dateutil",
|
|
@@ -226,9 +226,9 @@ allowlist_externals =
|
|
|
226
226
|
make
|
|
227
227
|
commands =
|
|
228
228
|
uv sync --group dev
|
|
229
|
-
pyathena: make test
|
|
230
|
-
sqla: make test
|
|
231
|
-
sqla_async: make test
|
|
229
|
+
pyathena: make test/pyathena
|
|
230
|
+
sqla: make test/sqla
|
|
231
|
+
sqla_async: make test/sqla-async
|
|
232
232
|
passenv =
|
|
233
233
|
TOXENV
|
|
234
234
|
AWS_*
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# file generated by setuptools-scm
|
|
2
|
-
# don't change, don't track in version control
|
|
3
|
-
|
|
4
|
-
__all__ = [
|
|
5
|
-
"__version__",
|
|
6
|
-
"__version_tuple__",
|
|
7
|
-
"version",
|
|
8
|
-
"version_tuple",
|
|
9
|
-
"__commit_id__",
|
|
10
|
-
"commit_id",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
TYPE_CHECKING = False
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from typing import Tuple
|
|
16
|
-
from typing import Union
|
|
17
|
-
|
|
18
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
-
COMMIT_ID = Union[str, None]
|
|
20
|
-
else:
|
|
21
|
-
VERSION_TUPLE = object
|
|
22
|
-
COMMIT_ID = object
|
|
23
|
-
|
|
24
|
-
version: str
|
|
25
|
-
__version__: str
|
|
26
|
-
__version_tuple__: VERSION_TUPLE
|
|
27
|
-
version_tuple: VERSION_TUPLE
|
|
28
|
-
commit_id: COMMIT_ID
|
|
29
|
-
__commit_id__: COMMIT_ID
|
|
30
|
-
|
|
31
|
-
__version__ = version = '3.30.0'
|
|
32
|
-
__version_tuple__ = version_tuple = (3, 30, 0)
|
|
33
|
-
|
|
34
|
-
__commit_id__ = commit_id = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|