sqlspec 0.26.0__py3-none-any.whl → 0.28.0__py3-none-any.whl

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.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (212) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +155 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +880 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +74 -2
  9. sqlspec/adapters/adbc/driver.py +226 -58
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +585 -0
  44. sqlspec/adapters/bigquery/config.py +36 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +489 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +563 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +225 -44
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1628 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +475 -86
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
  77. sqlspec/adapters/psqlpy/driver.py +108 -41
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +962 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +91 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +582 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +331 -62
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +55 -47
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +234 -47
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +183 -160
  129. sqlspec/driver/_common.py +197 -109
  130. sqlspec/driver/_sync.py +189 -161
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +69 -61
  142. sqlspec/extensions/fastapi/__init__.py +21 -0
  143. sqlspec/extensions/fastapi/extension.py +331 -0
  144. sqlspec/extensions/fastapi/providers.py +543 -0
  145. sqlspec/extensions/flask/__init__.py +36 -0
  146. sqlspec/extensions/flask/_state.py +71 -0
  147. sqlspec/extensions/flask/_utils.py +40 -0
  148. sqlspec/extensions/flask/extension.py +389 -0
  149. sqlspec/extensions/litestar/__init__.py +21 -4
  150. sqlspec/extensions/litestar/cli.py +54 -10
  151. sqlspec/extensions/litestar/config.py +56 -266
  152. sqlspec/extensions/litestar/handlers.py +46 -17
  153. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  154. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  155. sqlspec/extensions/litestar/plugin.py +349 -224
  156. sqlspec/extensions/litestar/providers.py +25 -25
  157. sqlspec/extensions/litestar/store.py +265 -0
  158. sqlspec/extensions/starlette/__init__.py +10 -0
  159. sqlspec/extensions/starlette/_state.py +25 -0
  160. sqlspec/extensions/starlette/_utils.py +52 -0
  161. sqlspec/extensions/starlette/extension.py +254 -0
  162. sqlspec/extensions/starlette/middleware.py +154 -0
  163. sqlspec/loader.py +30 -49
  164. sqlspec/migrations/base.py +200 -76
  165. sqlspec/migrations/commands.py +591 -62
  166. sqlspec/migrations/context.py +6 -9
  167. sqlspec/migrations/fix.py +199 -0
  168. sqlspec/migrations/loaders.py +47 -19
  169. sqlspec/migrations/runner.py +241 -75
  170. sqlspec/migrations/tracker.py +237 -21
  171. sqlspec/migrations/utils.py +51 -3
  172. sqlspec/migrations/validation.py +177 -0
  173. sqlspec/protocols.py +106 -36
  174. sqlspec/storage/_utils.py +85 -0
  175. sqlspec/storage/backends/fsspec.py +133 -107
  176. sqlspec/storage/backends/local.py +78 -51
  177. sqlspec/storage/backends/obstore.py +276 -168
  178. sqlspec/storage/registry.py +75 -39
  179. sqlspec/typing.py +30 -84
  180. sqlspec/utils/__init__.py +25 -4
  181. sqlspec/utils/arrow_helpers.py +81 -0
  182. sqlspec/utils/config_resolver.py +6 -6
  183. sqlspec/utils/correlation.py +4 -5
  184. sqlspec/utils/data_transformation.py +3 -2
  185. sqlspec/utils/deprecation.py +9 -8
  186. sqlspec/utils/fixtures.py +4 -4
  187. sqlspec/utils/logging.py +46 -6
  188. sqlspec/utils/module_loader.py +205 -5
  189. sqlspec/utils/portal.py +311 -0
  190. sqlspec/utils/schema.py +288 -0
  191. sqlspec/utils/serializers.py +113 -4
  192. sqlspec/utils/sync_tools.py +36 -22
  193. sqlspec/utils/text.py +1 -2
  194. sqlspec/utils/type_guards.py +136 -20
  195. sqlspec/utils/version.py +433 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
  197. sqlspec-0.28.0.dist-info/RECORD +221 -0
  198. sqlspec/builder/mixins/__init__.py +0 -55
  199. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  200. sqlspec/builder/mixins/_delete_operations.py +0 -50
  201. sqlspec/builder/mixins/_insert_operations.py +0 -282
  202. sqlspec/builder/mixins/_merge_operations.py +0 -698
  203. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  204. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  205. sqlspec/builder/mixins/_select_operations.py +0 -930
  206. sqlspec/builder/mixins/_update_operations.py +0 -199
  207. sqlspec/builder/mixins/_where_clause.py +0 -1298
  208. sqlspec-0.26.0.dist-info/RECORD +0 -157
  209. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  210. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
  211. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
  212. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,12 @@ No external dependencies like fsspec or obstore required.
7
7
  import shutil
8
8
  from collections.abc import AsyncIterator, Iterator
9
9
  from pathlib import Path
10
- from typing import TYPE_CHECKING, Any, Optional, Union
10
+ from typing import TYPE_CHECKING, Any
11
11
  from urllib.parse import unquote, urlparse
12
12
 
13
- from sqlspec.exceptions import MissingDependencyError
14
- from sqlspec.typing import PYARROW_INSTALLED
13
+ from mypy_extensions import mypyc_attr
14
+
15
+ from sqlspec.utils.module_loader import ensure_pyarrow
15
16
  from sqlspec.utils.sync_tools import async_
16
17
 
17
18
  if TYPE_CHECKING:
@@ -22,6 +23,28 @@ if TYPE_CHECKING:
22
23
  __all__ = ("LocalStore",)
23
24
 
24
25
 
26
+ class _LocalArrowIterator:
27
+ """Async iterator for LocalStore Arrow streaming."""
28
+
29
+ __slots__ = ("_sync_iter",)
30
+
31
+ def __init__(self, sync_iter: "Iterator[ArrowRecordBatch]") -> None:
32
+ self._sync_iter = sync_iter
33
+
34
+ def __aiter__(self) -> "_LocalArrowIterator":
35
+ return self
36
+
37
+ async def __anext__(self) -> "ArrowRecordBatch":
38
+ def _safe_next() -> "ArrowRecordBatch":
39
+ try:
40
+ return next(self._sync_iter)
41
+ except StopIteration as e:
42
+ raise StopAsyncIteration from e
43
+
44
+ return await async_(_safe_next)()
45
+
46
+
47
+ @mypyc_attr(allow_interpreted_subclasses=True)
25
48
  class LocalStore:
26
49
  """Simple local file system storage backend.
27
50
 
@@ -62,39 +85,41 @@ class LocalStore:
62
85
  elif self.base_path.is_file():
63
86
  # If base_path points to a file, use its parent as the base directory
64
87
  self.base_path = self.base_path.parent
65
- self._loop: Optional[asyncio.AbstractEventLoop] = None
88
+ self._loop: asyncio.AbstractEventLoop | None = None
66
89
 
67
90
  self.protocol = "file"
68
91
  self.backend_type = "local"
69
92
 
70
- def _ensure_pyarrow(self) -> None:
71
- """Ensure PyArrow is available for Arrow operations."""
72
- if not PYARROW_INSTALLED:
73
- raise MissingDependencyError(package="pyarrow", install_package="pyarrow")
93
+ def _resolve_path(self, path: "str | Path") -> Path:
94
+ """Resolve path relative to base_path.
74
95
 
75
- def _resolve_path(self, path: "Union[str, Path]") -> Path:
76
- """Resolve path relative to base_path."""
96
+ Args:
97
+ path: Path to resolve (absolute or relative).
98
+
99
+ Returns:
100
+ Resolved Path object.
101
+ """
77
102
  p = Path(path)
78
103
  if p.is_absolute():
79
104
  return p
80
105
  return self.base_path / p
81
106
 
82
- def read_bytes(self, path: "Union[str, Path]", **kwargs: Any) -> bytes:
107
+ def read_bytes(self, path: "str | Path", **kwargs: Any) -> bytes:
83
108
  """Read bytes from file."""
84
109
  resolved = self._resolve_path(path)
85
110
  return resolved.read_bytes()
86
111
 
87
- def write_bytes(self, path: "Union[str, Path]", data: bytes, **kwargs: Any) -> None:
112
+ def write_bytes(self, path: "str | Path", data: bytes, **kwargs: Any) -> None:
88
113
  """Write bytes to file."""
89
114
  resolved = self._resolve_path(path)
90
115
  resolved.parent.mkdir(parents=True, exist_ok=True)
91
116
  resolved.write_bytes(data)
92
117
 
93
- def read_text(self, path: "Union[str, Path]", encoding: str = "utf-8", **kwargs: Any) -> str:
118
+ def read_text(self, path: "str | Path", encoding: str = "utf-8", **kwargs: Any) -> str:
94
119
  """Read text from file."""
95
120
  return self._resolve_path(path).read_text(encoding=encoding)
96
121
 
97
- def write_text(self, path: "Union[str, Path]", data: str, encoding: str = "utf-8", **kwargs: Any) -> None:
122
+ def write_text(self, path: "str | Path", data: str, encoding: str = "utf-8", **kwargs: Any) -> None:
98
123
  """Write text to file."""
99
124
  resolved = self._resolve_path(path)
100
125
  resolved.parent.mkdir(parents=True, exist_ok=True)
@@ -131,11 +156,11 @@ class LocalStore:
131
156
 
132
157
  return sorted(files)
133
158
 
134
- def exists(self, path: "Union[str, Path]", **kwargs: Any) -> bool:
159
+ def exists(self, path: "str | Path", **kwargs: Any) -> bool:
135
160
  """Check if file exists."""
136
161
  return self._resolve_path(path).exists()
137
162
 
138
- def delete(self, path: "Union[str, Path]", **kwargs: Any) -> None:
163
+ def delete(self, path: "str | Path", **kwargs: Any) -> None:
139
164
  """Delete file or directory."""
140
165
  resolved = self._resolve_path(path)
141
166
  if resolved.is_dir():
@@ -143,7 +168,7 @@ class LocalStore:
143
168
  elif resolved.exists():
144
169
  resolved.unlink()
145
170
 
146
- def copy(self, source: "Union[str, Path]", destination: "Union[str, Path]", **kwargs: Any) -> None:
171
+ def copy(self, source: "str | Path", destination: "str | Path", **kwargs: Any) -> None:
147
172
  """Copy file or directory."""
148
173
  src = self._resolve_path(source)
149
174
  dst = self._resolve_path(destination)
@@ -154,7 +179,7 @@ class LocalStore:
154
179
  else:
155
180
  shutil.copy2(src, dst)
156
181
 
157
- def move(self, source: "Union[str, Path]", destination: "Union[str, Path]", **kwargs: Any) -> None:
182
+ def move(self, source: "str | Path", destination: "str | Path", **kwargs: Any) -> None:
158
183
  """Move file or directory."""
159
184
  src = self._resolve_path(source)
160
185
  dst = self._resolve_path(destination)
@@ -182,7 +207,7 @@ class LocalStore:
182
207
 
183
208
  return sorted(results)
184
209
 
185
- def get_metadata(self, path: "Union[str, Path]", **kwargs: Any) -> dict[str, Any]:
210
+ def get_metadata(self, path: "str | Path", **kwargs: Any) -> dict[str, Any]:
186
211
  """Get file metadata."""
187
212
  resolved = self._resolve_path(path)
188
213
  if not resolved.exists():
@@ -198,29 +223,29 @@ class LocalStore:
198
223
  "path": str(resolved),
199
224
  }
200
225
 
201
- def is_object(self, path: "Union[str, Path]") -> bool:
226
+ def is_object(self, path: "str | Path") -> bool:
202
227
  """Check if path points to a file."""
203
228
  return self._resolve_path(path).is_file()
204
229
 
205
- def is_path(self, path: "Union[str, Path]") -> bool:
230
+ def is_path(self, path: "str | Path") -> bool:
206
231
  """Check if path points to a directory."""
207
232
  return self._resolve_path(path).is_dir()
208
233
 
209
- def read_arrow(self, path: "Union[str, Path]", **kwargs: Any) -> "ArrowTable":
234
+ def read_arrow(self, path: "str | Path", **kwargs: Any) -> "ArrowTable":
210
235
  """Read Arrow table from file."""
211
- self._ensure_pyarrow()
236
+ ensure_pyarrow()
212
237
  import pyarrow.parquet as pq
213
238
 
214
- return pq.read_table(str(self._resolve_path(path)))
239
+ return pq.read_table(str(self._resolve_path(path))) # pyright: ignore
215
240
 
216
- def write_arrow(self, path: "Union[str, Path]", table: "ArrowTable", **kwargs: Any) -> None:
241
+ def write_arrow(self, path: "str | Path", table: "ArrowTable", **kwargs: Any) -> None:
217
242
  """Write Arrow table to file."""
218
- self._ensure_pyarrow()
243
+ ensure_pyarrow()
219
244
  import pyarrow.parquet as pq
220
245
 
221
246
  resolved = self._resolve_path(path)
222
247
  resolved.parent.mkdir(parents=True, exist_ok=True)
223
- pq.write_table(table, str(resolved))
248
+ pq.write_table(table, str(resolved)) # pyright: ignore
224
249
 
225
250
  def stream_arrow(self, pattern: str, **kwargs: Any) -> Iterator["ArrowRecordBatch"]:
226
251
  """Stream Arrow record batches from files matching pattern.
@@ -228,38 +253,35 @@ class LocalStore:
228
253
  Yields:
229
254
  Arrow record batches from matching files.
230
255
  """
231
- if not PYARROW_INSTALLED:
232
- raise MissingDependencyError(package="pyarrow", install_package="pyarrow")
256
+ ensure_pyarrow()
233
257
  import pyarrow.parquet as pq
234
258
 
235
259
  files = self.glob(pattern)
236
260
  for file_path in files:
237
261
  resolved = self._resolve_path(file_path)
238
262
  parquet_file = pq.ParquetFile(str(resolved))
239
- yield from parquet_file.iter_batches()
263
+ yield from parquet_file.iter_batches() # pyright: ignore
240
264
 
241
- def sign(self, path: "Union[str, Path]", expires_in: int = 3600, for_upload: bool = False) -> str:
265
+ def sign(self, path: "str | Path", expires_in: int = 3600, for_upload: bool = False) -> str:
242
266
  """Generate a signed URL (returns file:// URI for local files)."""
243
267
  # For local files, just return a file:// URI
244
268
  # No actual signing needed for local files
245
269
  return self._resolve_path(path).as_uri()
246
270
 
247
271
  # Async methods using sync_tools.async_
248
- async def read_bytes_async(self, path: "Union[str, Path]", **kwargs: Any) -> bytes:
272
+ async def read_bytes_async(self, path: "str | Path", **kwargs: Any) -> bytes:
249
273
  """Read bytes from file asynchronously."""
250
274
  return await async_(self.read_bytes)(path, **kwargs)
251
275
 
252
- async def write_bytes_async(self, path: "Union[str, Path]", data: bytes, **kwargs: Any) -> None:
276
+ async def write_bytes_async(self, path: "str | Path", data: bytes, **kwargs: Any) -> None:
253
277
  """Write bytes to file asynchronously."""
254
278
  await async_(self.write_bytes)(path, data, **kwargs)
255
279
 
256
- async def read_text_async(self, path: "Union[str, Path]", encoding: str = "utf-8", **kwargs: Any) -> str:
280
+ async def read_text_async(self, path: "str | Path", encoding: str = "utf-8", **kwargs: Any) -> str:
257
281
  """Read text from file asynchronously."""
258
282
  return await async_(self.read_text)(path, encoding, **kwargs)
259
283
 
260
- async def write_text_async(
261
- self, path: "Union[str, Path]", data: str, encoding: str = "utf-8", **kwargs: Any
262
- ) -> None:
284
+ async def write_text_async(self, path: "str | Path", data: str, encoding: str = "utf-8", **kwargs: Any) -> None:
263
285
  """Write text to file asynchronously."""
264
286
  await async_(self.write_text)(path, data, encoding, **kwargs)
265
287
 
@@ -267,44 +289,49 @@ class LocalStore:
267
289
  """List objects asynchronously."""
268
290
  return await async_(self.list_objects)(prefix, recursive, **kwargs)
269
291
 
270
- async def exists_async(self, path: "Union[str, Path]", **kwargs: Any) -> bool:
292
+ async def exists_async(self, path: "str | Path", **kwargs: Any) -> bool:
271
293
  """Check if file exists asynchronously."""
272
294
  return await async_(self.exists)(path, **kwargs)
273
295
 
274
- async def delete_async(self, path: "Union[str, Path]", **kwargs: Any) -> None:
296
+ async def delete_async(self, path: "str | Path", **kwargs: Any) -> None:
275
297
  """Delete file asynchronously."""
276
298
  await async_(self.delete)(path, **kwargs)
277
299
 
278
- async def copy_async(self, source: "Union[str, Path]", destination: "Union[str, Path]", **kwargs: Any) -> None:
300
+ async def copy_async(self, source: "str | Path", destination: "str | Path", **kwargs: Any) -> None:
279
301
  """Copy file asynchronously."""
280
302
  await async_(self.copy)(source, destination, **kwargs)
281
303
 
282
- async def move_async(self, source: "Union[str, Path]", destination: "Union[str, Path]", **kwargs: Any) -> None:
304
+ async def move_async(self, source: "str | Path", destination: "str | Path", **kwargs: Any) -> None:
283
305
  """Move file asynchronously."""
284
306
  await async_(self.move)(source, destination, **kwargs)
285
307
 
286
- async def get_metadata_async(self, path: "Union[str, Path]", **kwargs: Any) -> dict[str, Any]:
308
+ async def get_metadata_async(self, path: "str | Path", **kwargs: Any) -> dict[str, Any]:
287
309
  """Get file metadata asynchronously."""
288
310
  return await async_(self.get_metadata)(path, **kwargs)
289
311
 
290
- async def read_arrow_async(self, path: "Union[str, Path]", **kwargs: Any) -> "ArrowTable":
312
+ async def read_arrow_async(self, path: "str | Path", **kwargs: Any) -> "ArrowTable":
291
313
  """Read Arrow table asynchronously."""
292
314
  return await async_(self.read_arrow)(path, **kwargs)
293
315
 
294
- async def write_arrow_async(self, path: "Union[str, Path]", table: "ArrowTable", **kwargs: Any) -> None:
316
+ async def write_arrow_async(self, path: "str | Path", table: "ArrowTable", **kwargs: Any) -> None:
295
317
  """Write Arrow table asynchronously."""
296
318
  await async_(self.write_arrow)(path, table, **kwargs)
297
319
 
298
320
  def stream_arrow_async(self, pattern: str, **kwargs: Any) -> AsyncIterator["ArrowRecordBatch"]:
299
- """Stream Arrow record batches asynchronously."""
321
+ """Stream Arrow record batches asynchronously.
300
322
 
301
- # Convert sync iterator to async
302
- async def _stream() -> AsyncIterator["ArrowRecordBatch"]:
303
- for batch in self.stream_arrow(pattern, **kwargs):
304
- yield batch
323
+ Offloads blocking file I/O operations to thread pool for
324
+ non-blocking event loop execution.
305
325
 
306
- return _stream()
326
+ Args:
327
+ pattern: Glob pattern to match files.
328
+ **kwargs: Additional arguments passed to stream_arrow().
329
+
330
+ Returns:
331
+ Arrow record batches from matching files.
332
+ """
333
+ return _LocalArrowIterator(self.stream_arrow(pattern, **kwargs))
307
334
 
308
- async def sign_async(self, path: "Union[str, Path]", expires_in: int = 3600, for_upload: bool = False) -> str:
335
+ async def sign_async(self, path: "str | Path", expires_in: int = 3600, for_upload: bool = False) -> str:
309
336
  """Generate a signed URL asynchronously (returns file:// URI for local files)."""
310
337
  return await async_(self.sign)(path, expires_in, for_upload)