PyAthena 3.28.0__tar.gz → 3.29.1__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 (86) hide show
  1. {pyathena-3.28.0 → pyathena-3.29.1}/PKG-INFO +19 -1
  2. {pyathena-3.28.0 → pyathena-3.29.1}/README.md +16 -0
  3. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/__init__.py +30 -0
  4. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/_version.py +2 -2
  5. pyathena-3.29.1/pyathena/aio/arrow/cursor.py +230 -0
  6. pyathena-3.29.1/pyathena/aio/common.py +500 -0
  7. pyathena-3.29.1/pyathena/aio/connection.py +54 -0
  8. pyathena-3.29.1/pyathena/aio/cursor.py +204 -0
  9. pyathena-3.29.1/pyathena/aio/pandas/cursor.py +251 -0
  10. pyathena-3.29.1/pyathena/aio/polars/cursor.py +238 -0
  11. pyathena-3.29.1/pyathena/aio/result_set.py +203 -0
  12. pyathena-3.29.1/pyathena/aio/s3fs/cursor.py +200 -0
  13. pyathena-3.29.1/pyathena/aio/spark/__init__.py +1 -0
  14. pyathena-3.29.1/pyathena/aio/spark/cursor.py +285 -0
  15. pyathena-3.29.1/pyathena/aio/sqlalchemy/__init__.py +1 -0
  16. pyathena-3.29.1/pyathena/aio/sqlalchemy/arrow.py +55 -0
  17. pyathena-3.29.1/pyathena/aio/sqlalchemy/base.py +221 -0
  18. pyathena-3.29.1/pyathena/aio/sqlalchemy/pandas.py +61 -0
  19. pyathena-3.29.1/pyathena/aio/sqlalchemy/polars.py +55 -0
  20. pyathena-3.29.1/pyathena/aio/sqlalchemy/rest.py +37 -0
  21. pyathena-3.29.1/pyathena/aio/sqlalchemy/s3fs.py +44 -0
  22. pyathena-3.29.1/pyathena/aio/util.py +33 -0
  23. pyathena-3.29.1/pyathena/arrow/__init__.py +1 -0
  24. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/arrow/async_cursor.py +2 -13
  25. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/arrow/cursor.py +6 -98
  26. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/async_cursor.py +1 -2
  27. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/common.py +70 -10
  28. pyathena-3.29.1/pyathena/cursor.py +181 -0
  29. pyathena-3.29.1/pyathena/filesystem/__init__.py +1 -0
  30. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/formatter.py +2 -2
  31. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/async_cursor.py +2 -13
  32. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/cursor.py +4 -98
  33. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/async_cursor.py +2 -13
  34. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/cursor.py +5 -152
  35. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/result_set.py +140 -2
  36. pyathena-3.29.1/pyathena/s3fs/__init__.py +1 -0
  37. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/s3fs/cursor.py +5 -140
  38. pyathena-3.29.1/pyathena/spark/__init__.py +1 -0
  39. pyathena-3.29.1/pyathena/sqlalchemy/__init__.py +1 -0
  40. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/compiler.py +65 -69
  41. {pyathena-3.28.0 → pyathena-3.29.1}/pyproject.toml +13 -1
  42. pyathena-3.28.0/pyathena/cursor.py +0 -394
  43. {pyathena-3.28.0 → pyathena-3.29.1}/.gitignore +0 -0
  44. {pyathena-3.28.0 → pyathena-3.29.1}/LICENSE +0 -0
  45. {pyathena-3.28.0/pyathena/arrow → pyathena-3.29.1/pyathena/aio}/__init__.py +0 -0
  46. {pyathena-3.28.0/pyathena/filesystem → pyathena-3.29.1/pyathena/aio/arrow}/__init__.py +0 -0
  47. {pyathena-3.28.0/pyathena/s3fs → pyathena-3.29.1/pyathena/aio/pandas}/__init__.py +0 -0
  48. {pyathena-3.28.0/pyathena/spark → pyathena-3.29.1/pyathena/aio/polars}/__init__.py +0 -0
  49. {pyathena-3.28.0/pyathena/sqlalchemy → pyathena-3.29.1/pyathena/aio/s3fs}/__init__.py +0 -0
  50. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/arrow/converter.py +0 -0
  51. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/arrow/result_set.py +0 -0
  52. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/arrow/util.py +0 -0
  53. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/connection.py +0 -0
  54. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/converter.py +0 -0
  55. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/error.py +0 -0
  56. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/filesystem/s3.py +0 -0
  57. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/filesystem/s3_object.py +0 -0
  58. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/model.py +0 -0
  59. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/__init__.py +0 -0
  60. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/converter.py +0 -0
  61. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/result_set.py +0 -0
  62. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/pandas/util.py +0 -0
  63. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/__init__.py +0 -0
  64. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/converter.py +0 -0
  65. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/result_set.py +0 -0
  66. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/polars/util.py +0 -0
  67. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/py.typed +0 -0
  68. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/s3fs/async_cursor.py +0 -0
  69. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/s3fs/converter.py +0 -0
  70. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/s3fs/reader.py +0 -0
  71. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/s3fs/result_set.py +0 -0
  72. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/spark/async_cursor.py +0 -0
  73. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/spark/common.py +0 -0
  74. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/spark/cursor.py +0 -0
  75. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/arrow.py +0 -0
  76. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/base.py +0 -0
  77. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/constants.py +0 -0
  78. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/pandas.py +0 -0
  79. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/polars.py +0 -0
  80. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/preparer.py +0 -0
  81. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/requirements.py +0 -0
  82. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/rest.py +0 -0
  83. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/s3fs.py +0 -0
  84. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/types.py +0 -0
  85. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/sqlalchemy/util.py +0 -0
  86. {pyathena-3.28.0 → pyathena-3.29.1}/pyathena/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyAthena
3
- Version: 3.28.0
3
+ Version: 3.29.1
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/
@@ -43,6 +43,8 @@ Requires-Dist: botocore>=1.29.4
43
43
  Requires-Dist: fsspec
44
44
  Requires-Dist: python-dateutil
45
45
  Requires-Dist: tenacity>=4.1.0
46
+ Provides-Extra: aiosqlalchemy
47
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.0; extra == 'aiosqlalchemy'
46
48
  Provides-Extra: arrow
47
49
  Requires-Dist: pyarrow>=10.0.0; (python_version < '3.14') and extra == 'arrow'
48
50
  Requires-Dist: pyarrow>=22.0.0; (python_version >= '3.14') and extra == 'arrow'
@@ -109,6 +111,22 @@ print(cursor.description)
109
111
  print(cursor.fetchall())
110
112
  ```
111
113
 
114
+ Native asyncio is also supported:
115
+
116
+ ```python
117
+ import asyncio
118
+ from pyathena import aconnect
119
+
120
+ async def main():
121
+ async with await aconnect(s3_staging_dir="s3://YOUR_S3_BUCKET/path/to/",
122
+ region_name="us-west-2") as conn:
123
+ cursor = conn.cursor()
124
+ await cursor.execute("SELECT 1")
125
+ print(await cursor.fetchone())
126
+
127
+ asyncio.run(main())
128
+ ```
129
+
112
130
  ## License
113
131
 
114
132
  [MIT license](LICENSE)
@@ -52,6 +52,22 @@ print(cursor.description)
52
52
  print(cursor.fetchall())
53
53
  ```
54
54
 
55
+ Native asyncio is also supported:
56
+
57
+ ```python
58
+ import asyncio
59
+ from pyathena import aconnect
60
+
61
+ async def main():
62
+ async with await aconnect(s3_staging_dir="s3://YOUR_S3_BUCKET/path/to/",
63
+ region_name="us-west-2") as conn:
64
+ cursor = conn.cursor()
65
+ await cursor.execute("SELECT 1")
66
+ print(await cursor.fetchone())
67
+
68
+ asyncio.run(main())
69
+ ```
70
+
55
71
  ## License
56
72
 
57
73
  [MIT license](LICENSE)
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any, FrozenSet, Type, overload
7
7
  from pyathena.error import * # noqa
8
8
 
9
9
  if TYPE_CHECKING:
10
+ from pyathena.aio.connection import AioConnection
10
11
  from pyathena.connection import Connection, ConnectionCursor
11
12
  from pyathena.cursor import Cursor
12
13
 
@@ -128,3 +129,32 @@ def connect(*args, **kwargs) -> "Connection[Any]":
128
129
  from pyathena.connection import Connection
129
130
 
130
131
  return Connection(*args, **kwargs)
132
+
133
+
134
+ async def aconnect(*args, **kwargs) -> "AioConnection":
135
+ """Create a new async database connection to Amazon Athena.
136
+
137
+ This is the async counterpart of :func:`connect`. It returns an
138
+ ``AioConnection`` whose cursors use native ``asyncio`` for polling
139
+ and API calls, keeping the event loop free.
140
+
141
+ Args:
142
+ **kwargs: Arguments forwarded to ``AioConnection.create()``.
143
+ See :func:`connect` for the full list of supported arguments.
144
+
145
+ Returns:
146
+ An ``AioConnection`` that produces ``AioCursor`` instances by default.
147
+
148
+ Example:
149
+ >>> import pyathena
150
+ >>> conn = await pyathena.aconnect(
151
+ ... s3_staging_dir='s3://my-bucket/staging/',
152
+ ... region_name='us-east-1',
153
+ ... )
154
+ >>> async with conn.cursor() as cursor:
155
+ ... await cursor.execute("SELECT 1")
156
+ ... print(await cursor.fetchone())
157
+ """
158
+ from pyathena.aio.connection import AioConnection
159
+
160
+ return await AioConnection.create(*args, **kwargs)
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '3.28.0'
32
- __version_tuple__ = version_tuple = (3, 28, 0)
31
+ __version__ = version = '3.29.1'
32
+ __version_tuple__ = version_tuple = (3, 29, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,230 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import logging
6
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast
7
+
8
+ from pyathena.aio.common import WithAsyncFetch
9
+ from pyathena.arrow.converter import (
10
+ DefaultArrowTypeConverter,
11
+ DefaultArrowUnloadTypeConverter,
12
+ )
13
+ from pyathena.arrow.result_set import AthenaArrowResultSet
14
+ from pyathena.common import CursorIterator
15
+ from pyathena.error import OperationalError, ProgrammingError
16
+ from pyathena.model import AthenaQueryExecution
17
+
18
+ if TYPE_CHECKING:
19
+ import polars as pl
20
+ from pyarrow import Table
21
+
22
+ _logger = logging.getLogger(__name__) # type: ignore
23
+
24
+
25
+ class AioArrowCursor(WithAsyncFetch):
26
+ """Native asyncio cursor that returns results as Apache Arrow Tables.
27
+
28
+ Uses ``asyncio.to_thread()`` for both result set creation and fetch
29
+ operations, keeping the event loop free.
30
+
31
+ Example:
32
+ >>> async with await pyathena.aconnect(...) as conn:
33
+ ... cursor = conn.cursor(AioArrowCursor)
34
+ ... await cursor.execute("SELECT * FROM my_table")
35
+ ... table = cursor.as_arrow()
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ s3_staging_dir: Optional[str] = None,
41
+ schema_name: Optional[str] = None,
42
+ catalog_name: Optional[str] = None,
43
+ work_group: Optional[str] = None,
44
+ poll_interval: float = 1,
45
+ encryption_option: Optional[str] = None,
46
+ kms_key: Optional[str] = None,
47
+ kill_on_interrupt: bool = True,
48
+ unload: bool = False,
49
+ result_reuse_enable: bool = False,
50
+ result_reuse_minutes: int = CursorIterator.DEFAULT_RESULT_REUSE_MINUTES,
51
+ connect_timeout: Optional[float] = None,
52
+ request_timeout: Optional[float] = None,
53
+ **kwargs,
54
+ ) -> None:
55
+ super().__init__(
56
+ s3_staging_dir=s3_staging_dir,
57
+ schema_name=schema_name,
58
+ catalog_name=catalog_name,
59
+ work_group=work_group,
60
+ poll_interval=poll_interval,
61
+ encryption_option=encryption_option,
62
+ kms_key=kms_key,
63
+ kill_on_interrupt=kill_on_interrupt,
64
+ result_reuse_enable=result_reuse_enable,
65
+ result_reuse_minutes=result_reuse_minutes,
66
+ **kwargs,
67
+ )
68
+ self._unload = unload
69
+ self._connect_timeout = connect_timeout
70
+ self._request_timeout = request_timeout
71
+ self._result_set: Optional[AthenaArrowResultSet] = None
72
+
73
+ @staticmethod
74
+ def get_default_converter(
75
+ unload: bool = False,
76
+ ) -> Union[DefaultArrowTypeConverter, DefaultArrowUnloadTypeConverter, Any]:
77
+ if unload:
78
+ return DefaultArrowUnloadTypeConverter()
79
+ return DefaultArrowTypeConverter()
80
+
81
+ async def execute( # type: ignore[override]
82
+ self,
83
+ operation: str,
84
+ parameters: Optional[Union[Dict[str, Any], List[str]]] = None,
85
+ work_group: Optional[str] = None,
86
+ s3_staging_dir: Optional[str] = None,
87
+ cache_size: Optional[int] = 0,
88
+ cache_expiration_time: Optional[int] = 0,
89
+ result_reuse_enable: Optional[bool] = None,
90
+ result_reuse_minutes: Optional[int] = None,
91
+ paramstyle: Optional[str] = None,
92
+ **kwargs,
93
+ ) -> "AioArrowCursor":
94
+ """Execute a SQL query asynchronously and return results as Arrow Tables.
95
+
96
+ Args:
97
+ operation: SQL query string to execute.
98
+ parameters: Query parameters for parameterized queries.
99
+ work_group: Athena workgroup to use for this query.
100
+ s3_staging_dir: S3 location for query results.
101
+ cache_size: Number of queries to check for result caching.
102
+ cache_expiration_time: Cache expiration time in seconds.
103
+ result_reuse_enable: Enable Athena result reuse for this query.
104
+ result_reuse_minutes: Minutes to reuse cached results.
105
+ paramstyle: Parameter style ('qmark' or 'pyformat').
106
+ **kwargs: Additional execution parameters.
107
+
108
+ Returns:
109
+ Self reference for method chaining.
110
+ """
111
+ self._reset_state()
112
+ operation, unload_location = self._prepare_unload(operation, s3_staging_dir)
113
+ self.query_id = await self._execute(
114
+ operation,
115
+ parameters=parameters,
116
+ work_group=work_group,
117
+ s3_staging_dir=s3_staging_dir,
118
+ cache_size=cache_size,
119
+ cache_expiration_time=cache_expiration_time,
120
+ result_reuse_enable=result_reuse_enable,
121
+ result_reuse_minutes=result_reuse_minutes,
122
+ paramstyle=paramstyle,
123
+ )
124
+
125
+ query_execution = await self._poll(self.query_id)
126
+ if query_execution.state == AthenaQueryExecution.STATE_SUCCEEDED:
127
+ self.result_set = await asyncio.to_thread(
128
+ AthenaArrowResultSet,
129
+ connection=self._connection,
130
+ converter=self._converter,
131
+ query_execution=query_execution,
132
+ arraysize=self.arraysize,
133
+ retry_config=self._retry_config,
134
+ unload=self._unload,
135
+ unload_location=unload_location,
136
+ connect_timeout=self._connect_timeout,
137
+ request_timeout=self._request_timeout,
138
+ **kwargs,
139
+ )
140
+ else:
141
+ raise OperationalError(query_execution.state_change_reason)
142
+ return self
143
+
144
+ async def fetchone( # type: ignore[override]
145
+ self,
146
+ ) -> Optional[Union[Tuple[Optional[Any], ...], Dict[Any, Optional[Any]]]]:
147
+ """Fetch the next row of the result set.
148
+
149
+ Wraps the synchronous fetch in ``asyncio.to_thread`` to avoid
150
+ blocking the event loop.
151
+
152
+ Returns:
153
+ A tuple representing the next row, or None if no more rows.
154
+
155
+ Raises:
156
+ ProgrammingError: If no result set is available.
157
+ """
158
+ if not self.has_result_set:
159
+ raise ProgrammingError("No result set.")
160
+ result_set = cast(AthenaArrowResultSet, self.result_set)
161
+ return await asyncio.to_thread(result_set.fetchone)
162
+
163
+ async def fetchmany( # type: ignore[override]
164
+ self, size: Optional[int] = None
165
+ ) -> List[Union[Tuple[Optional[Any], ...], Dict[Any, Optional[Any]]]]:
166
+ """Fetch multiple rows from the result set.
167
+
168
+ Wraps the synchronous fetch in ``asyncio.to_thread`` to avoid
169
+ blocking the event loop.
170
+
171
+ Args:
172
+ size: Maximum number of rows to fetch. Defaults to arraysize.
173
+
174
+ Returns:
175
+ List of tuples representing the fetched rows.
176
+
177
+ Raises:
178
+ ProgrammingError: If no result set is available.
179
+ """
180
+ if not self.has_result_set:
181
+ raise ProgrammingError("No result set.")
182
+ result_set = cast(AthenaArrowResultSet, self.result_set)
183
+ return await asyncio.to_thread(result_set.fetchmany, size)
184
+
185
+ async def fetchall( # type: ignore[override]
186
+ self,
187
+ ) -> List[Union[Tuple[Optional[Any], ...], Dict[Any, Optional[Any]]]]:
188
+ """Fetch all remaining rows from the result set.
189
+
190
+ Wraps the synchronous fetch in ``asyncio.to_thread`` to avoid
191
+ blocking the event loop.
192
+
193
+ Returns:
194
+ List of tuples representing all remaining rows.
195
+
196
+ Raises:
197
+ ProgrammingError: If no result set is available.
198
+ """
199
+ if not self.has_result_set:
200
+ raise ProgrammingError("No result set.")
201
+ result_set = cast(AthenaArrowResultSet, self.result_set)
202
+ return await asyncio.to_thread(result_set.fetchall)
203
+
204
+ async def __anext__(self):
205
+ row = await self.fetchone()
206
+ if row is None:
207
+ raise StopAsyncIteration
208
+ return row
209
+
210
+ def as_arrow(self) -> "Table":
211
+ """Return query results as an Apache Arrow Table.
212
+
213
+ Returns:
214
+ Apache Arrow Table containing all query results.
215
+ """
216
+ if not self.has_result_set:
217
+ raise ProgrammingError("No result set.")
218
+ result_set = cast(AthenaArrowResultSet, self.result_set)
219
+ return result_set.as_arrow()
220
+
221
+ def as_polars(self) -> "pl.DataFrame":
222
+ """Return query results as a Polars DataFrame.
223
+
224
+ Returns:
225
+ Polars DataFrame containing all query results.
226
+ """
227
+ if not self.has_result_set:
228
+ raise ProgrammingError("No result set.")
229
+ result_set = cast(AthenaArrowResultSet, self.result_set)
230
+ return result_set.as_polars()