polars-runtime-compat 1.34.0b2__cp39-abi3-macosx_10_12_x86_64.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 polars-runtime-compat might be problematic. Click here for more details.

Files changed (203) hide show
  1. _polars_runtime_compat/.gitkeep +0 -0
  2. _polars_runtime_compat/_polars_runtime_compat.abi3.so +0 -0
  3. polars/__init__.py +528 -0
  4. polars/_cpu_check.py +265 -0
  5. polars/_dependencies.py +355 -0
  6. polars/_plr.py +99 -0
  7. polars/_plr.pyi +2496 -0
  8. polars/_reexport.py +23 -0
  9. polars/_typing.py +478 -0
  10. polars/_utils/__init__.py +37 -0
  11. polars/_utils/async_.py +102 -0
  12. polars/_utils/cache.py +176 -0
  13. polars/_utils/cloud.py +40 -0
  14. polars/_utils/constants.py +29 -0
  15. polars/_utils/construction/__init__.py +46 -0
  16. polars/_utils/construction/dataframe.py +1397 -0
  17. polars/_utils/construction/other.py +72 -0
  18. polars/_utils/construction/series.py +560 -0
  19. polars/_utils/construction/utils.py +118 -0
  20. polars/_utils/convert.py +224 -0
  21. polars/_utils/deprecation.py +406 -0
  22. polars/_utils/getitem.py +457 -0
  23. polars/_utils/logging.py +11 -0
  24. polars/_utils/nest_asyncio.py +264 -0
  25. polars/_utils/parquet.py +15 -0
  26. polars/_utils/parse/__init__.py +12 -0
  27. polars/_utils/parse/expr.py +242 -0
  28. polars/_utils/polars_version.py +19 -0
  29. polars/_utils/pycapsule.py +53 -0
  30. polars/_utils/scan.py +27 -0
  31. polars/_utils/serde.py +63 -0
  32. polars/_utils/slice.py +215 -0
  33. polars/_utils/udfs.py +1251 -0
  34. polars/_utils/unstable.py +63 -0
  35. polars/_utils/various.py +782 -0
  36. polars/_utils/wrap.py +25 -0
  37. polars/api.py +370 -0
  38. polars/catalog/__init__.py +0 -0
  39. polars/catalog/unity/__init__.py +19 -0
  40. polars/catalog/unity/client.py +733 -0
  41. polars/catalog/unity/models.py +152 -0
  42. polars/config.py +1571 -0
  43. polars/convert/__init__.py +25 -0
  44. polars/convert/general.py +1046 -0
  45. polars/convert/normalize.py +261 -0
  46. polars/dataframe/__init__.py +5 -0
  47. polars/dataframe/_html.py +186 -0
  48. polars/dataframe/frame.py +12582 -0
  49. polars/dataframe/group_by.py +1067 -0
  50. polars/dataframe/plotting.py +257 -0
  51. polars/datatype_expr/__init__.py +5 -0
  52. polars/datatype_expr/array.py +56 -0
  53. polars/datatype_expr/datatype_expr.py +304 -0
  54. polars/datatype_expr/list.py +18 -0
  55. polars/datatype_expr/struct.py +69 -0
  56. polars/datatypes/__init__.py +122 -0
  57. polars/datatypes/_parse.py +195 -0
  58. polars/datatypes/_utils.py +48 -0
  59. polars/datatypes/classes.py +1213 -0
  60. polars/datatypes/constants.py +11 -0
  61. polars/datatypes/constructor.py +172 -0
  62. polars/datatypes/convert.py +366 -0
  63. polars/datatypes/group.py +130 -0
  64. polars/exceptions.py +230 -0
  65. polars/expr/__init__.py +7 -0
  66. polars/expr/array.py +964 -0
  67. polars/expr/binary.py +346 -0
  68. polars/expr/categorical.py +306 -0
  69. polars/expr/datetime.py +2620 -0
  70. polars/expr/expr.py +11272 -0
  71. polars/expr/list.py +1408 -0
  72. polars/expr/meta.py +444 -0
  73. polars/expr/name.py +321 -0
  74. polars/expr/string.py +3045 -0
  75. polars/expr/struct.py +357 -0
  76. polars/expr/whenthen.py +185 -0
  77. polars/functions/__init__.py +193 -0
  78. polars/functions/aggregation/__init__.py +33 -0
  79. polars/functions/aggregation/horizontal.py +298 -0
  80. polars/functions/aggregation/vertical.py +341 -0
  81. polars/functions/as_datatype.py +848 -0
  82. polars/functions/business.py +138 -0
  83. polars/functions/col.py +384 -0
  84. polars/functions/datatype.py +121 -0
  85. polars/functions/eager.py +524 -0
  86. polars/functions/escape_regex.py +29 -0
  87. polars/functions/lazy.py +2751 -0
  88. polars/functions/len.py +68 -0
  89. polars/functions/lit.py +210 -0
  90. polars/functions/random.py +22 -0
  91. polars/functions/range/__init__.py +19 -0
  92. polars/functions/range/_utils.py +15 -0
  93. polars/functions/range/date_range.py +303 -0
  94. polars/functions/range/datetime_range.py +370 -0
  95. polars/functions/range/int_range.py +348 -0
  96. polars/functions/range/linear_space.py +311 -0
  97. polars/functions/range/time_range.py +287 -0
  98. polars/functions/repeat.py +301 -0
  99. polars/functions/whenthen.py +353 -0
  100. polars/interchange/__init__.py +10 -0
  101. polars/interchange/buffer.py +77 -0
  102. polars/interchange/column.py +190 -0
  103. polars/interchange/dataframe.py +230 -0
  104. polars/interchange/from_dataframe.py +328 -0
  105. polars/interchange/protocol.py +303 -0
  106. polars/interchange/utils.py +170 -0
  107. polars/io/__init__.py +64 -0
  108. polars/io/_utils.py +317 -0
  109. polars/io/avro.py +49 -0
  110. polars/io/clipboard.py +36 -0
  111. polars/io/cloud/__init__.py +17 -0
  112. polars/io/cloud/_utils.py +80 -0
  113. polars/io/cloud/credential_provider/__init__.py +17 -0
  114. polars/io/cloud/credential_provider/_builder.py +520 -0
  115. polars/io/cloud/credential_provider/_providers.py +618 -0
  116. polars/io/csv/__init__.py +9 -0
  117. polars/io/csv/_utils.py +38 -0
  118. polars/io/csv/batched_reader.py +142 -0
  119. polars/io/csv/functions.py +1495 -0
  120. polars/io/database/__init__.py +6 -0
  121. polars/io/database/_arrow_registry.py +70 -0
  122. polars/io/database/_cursor_proxies.py +147 -0
  123. polars/io/database/_executor.py +578 -0
  124. polars/io/database/_inference.py +314 -0
  125. polars/io/database/_utils.py +144 -0
  126. polars/io/database/functions.py +516 -0
  127. polars/io/delta.py +499 -0
  128. polars/io/iceberg/__init__.py +3 -0
  129. polars/io/iceberg/_utils.py +697 -0
  130. polars/io/iceberg/dataset.py +556 -0
  131. polars/io/iceberg/functions.py +151 -0
  132. polars/io/ipc/__init__.py +8 -0
  133. polars/io/ipc/functions.py +514 -0
  134. polars/io/json/__init__.py +3 -0
  135. polars/io/json/read.py +101 -0
  136. polars/io/ndjson.py +332 -0
  137. polars/io/parquet/__init__.py +17 -0
  138. polars/io/parquet/field_overwrites.py +140 -0
  139. polars/io/parquet/functions.py +722 -0
  140. polars/io/partition.py +491 -0
  141. polars/io/plugins.py +187 -0
  142. polars/io/pyarrow_dataset/__init__.py +5 -0
  143. polars/io/pyarrow_dataset/anonymous_scan.py +109 -0
  144. polars/io/pyarrow_dataset/functions.py +79 -0
  145. polars/io/scan_options/__init__.py +5 -0
  146. polars/io/scan_options/_options.py +59 -0
  147. polars/io/scan_options/cast_options.py +126 -0
  148. polars/io/spreadsheet/__init__.py +6 -0
  149. polars/io/spreadsheet/_utils.py +52 -0
  150. polars/io/spreadsheet/_write_utils.py +647 -0
  151. polars/io/spreadsheet/functions.py +1323 -0
  152. polars/lazyframe/__init__.py +9 -0
  153. polars/lazyframe/engine_config.py +61 -0
  154. polars/lazyframe/frame.py +8564 -0
  155. polars/lazyframe/group_by.py +669 -0
  156. polars/lazyframe/in_process.py +42 -0
  157. polars/lazyframe/opt_flags.py +333 -0
  158. polars/meta/__init__.py +14 -0
  159. polars/meta/build.py +33 -0
  160. polars/meta/index_type.py +27 -0
  161. polars/meta/thread_pool.py +50 -0
  162. polars/meta/versions.py +120 -0
  163. polars/ml/__init__.py +0 -0
  164. polars/ml/torch.py +213 -0
  165. polars/ml/utilities.py +30 -0
  166. polars/plugins.py +155 -0
  167. polars/py.typed +0 -0
  168. polars/pyproject.toml +96 -0
  169. polars/schema.py +265 -0
  170. polars/selectors.py +3117 -0
  171. polars/series/__init__.py +5 -0
  172. polars/series/array.py +776 -0
  173. polars/series/binary.py +254 -0
  174. polars/series/categorical.py +246 -0
  175. polars/series/datetime.py +2275 -0
  176. polars/series/list.py +1087 -0
  177. polars/series/plotting.py +191 -0
  178. polars/series/series.py +9197 -0
  179. polars/series/string.py +2367 -0
  180. polars/series/struct.py +154 -0
  181. polars/series/utils.py +191 -0
  182. polars/sql/__init__.py +7 -0
  183. polars/sql/context.py +677 -0
  184. polars/sql/functions.py +139 -0
  185. polars/string_cache.py +185 -0
  186. polars/testing/__init__.py +13 -0
  187. polars/testing/asserts/__init__.py +9 -0
  188. polars/testing/asserts/frame.py +231 -0
  189. polars/testing/asserts/series.py +219 -0
  190. polars/testing/asserts/utils.py +12 -0
  191. polars/testing/parametric/__init__.py +33 -0
  192. polars/testing/parametric/profiles.py +107 -0
  193. polars/testing/parametric/strategies/__init__.py +22 -0
  194. polars/testing/parametric/strategies/_utils.py +14 -0
  195. polars/testing/parametric/strategies/core.py +615 -0
  196. polars/testing/parametric/strategies/data.py +452 -0
  197. polars/testing/parametric/strategies/dtype.py +436 -0
  198. polars/testing/parametric/strategies/legacy.py +169 -0
  199. polars/type_aliases.py +24 -0
  200. polars_runtime_compat-1.34.0b2.dist-info/METADATA +190 -0
  201. polars_runtime_compat-1.34.0b2.dist-info/RECORD +203 -0
  202. polars_runtime_compat-1.34.0b2.dist-info/WHEEL +4 -0
  203. polars_runtime_compat-1.34.0b2.dist-info/licenses/LICENSE +20 -0
@@ -0,0 +1,314 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+ import re
5
+ from contextlib import suppress
6
+ from inspect import isclass
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from polars.datatypes import (
10
+ Binary,
11
+ Boolean,
12
+ Date,
13
+ Datetime,
14
+ Decimal,
15
+ Duration,
16
+ Float32,
17
+ Float64,
18
+ Int8,
19
+ Int16,
20
+ Int32,
21
+ Int64,
22
+ Int128,
23
+ List,
24
+ Null,
25
+ String,
26
+ Time,
27
+ UInt8,
28
+ UInt16,
29
+ UInt32,
30
+ UInt64,
31
+ )
32
+ from polars.datatypes._parse import parse_py_type_into_dtype
33
+ from polars.datatypes.group import (
34
+ INTEGER_DTYPES,
35
+ UNSIGNED_INTEGER_DTYPES,
36
+ )
37
+
38
+ if TYPE_CHECKING:
39
+ from polars._typing import PolarsDataType
40
+
41
+
42
+ def dtype_from_database_typename(
43
+ value: str,
44
+ *,
45
+ raise_unmatched: bool = True,
46
+ ) -> PolarsDataType | None:
47
+ """
48
+ Attempt to infer Polars dtype from database cursor `type_code` string value.
49
+
50
+ Examples
51
+ --------
52
+ >>> dtype_from_database_typename("INT2")
53
+ Int16
54
+ >>> dtype_from_database_typename("NVARCHAR")
55
+ String
56
+ >>> dtype_from_database_typename("NUMERIC(10,2)")
57
+ Decimal(precision=10, scale=2)
58
+ >>> dtype_from_database_typename("TIMESTAMP WITHOUT TZ")
59
+ Datetime(time_unit='us', time_zone=None)
60
+ """
61
+ dtype: PolarsDataType | None = None
62
+
63
+ # normalise string name/case (eg: 'IntegerType' -> 'INTEGER')
64
+ original_value = value
65
+ value = value.upper().replace("TYPE", "")
66
+
67
+ # extract optional type modifier (eg: 'VARCHAR(64)' -> '64')
68
+ if re.search(r"\([\w,: ]+\)$", value):
69
+ modifier = value[value.find("(") + 1 : -1]
70
+ value = value.split("(")[0]
71
+ elif (
72
+ not value.startswith(("<", ">")) and re.search(r"\[[\w,\]\[: ]+]$", value)
73
+ ) or value.endswith(("[S]", "[MS]", "[US]", "[NS]")):
74
+ modifier = value[value.find("[") + 1 : -1]
75
+ value = value.split("[")[0]
76
+ else:
77
+ modifier = ""
78
+
79
+ # array dtypes
80
+ array_aliases = ("ARRAY", "LIST", "[]")
81
+ if value.endswith(array_aliases) or value.startswith(array_aliases):
82
+ for a in array_aliases:
83
+ value = value.replace(a, "", 1) if value else ""
84
+
85
+ nested: PolarsDataType | None = None
86
+ if not value and modifier:
87
+ nested = dtype_from_database_typename(
88
+ value=modifier,
89
+ raise_unmatched=False,
90
+ )
91
+ else:
92
+ if inner_value := dtype_from_database_typename(
93
+ value[1:-1]
94
+ if (value[0], value[-1]) == ("<", ">")
95
+ else re.sub(r"\W", "", re.sub(r"\WOF\W", "", value)),
96
+ raise_unmatched=False,
97
+ ):
98
+ nested = inner_value
99
+ elif modifier:
100
+ nested = dtype_from_database_typename(
101
+ value=modifier,
102
+ raise_unmatched=False,
103
+ )
104
+ if nested:
105
+ dtype = List(nested)
106
+
107
+ # float dtypes
108
+ elif value.startswith("FLOAT") or ("DOUBLE" in value) or (value == "REAL"):
109
+ dtype = (
110
+ Float32
111
+ if value == "FLOAT4"
112
+ or (value.endswith(("16", "32")) or (modifier in ("16", "32")))
113
+ else Float64
114
+ )
115
+
116
+ # integer dtypes
117
+ elif ("INTERVAL" not in value) and (
118
+ value.startswith(("INT", "UINT", "UNSIGNED"))
119
+ or value.endswith(("INT", "SERIAL"))
120
+ or ("INTEGER" in value)
121
+ or value in ("TINY", "SHORT", "LONG", "LONGLONG", "ROWID")
122
+ ):
123
+ sz: Any
124
+ if "HUGEINT" in value:
125
+ sz = 128
126
+ elif (
127
+ "LARGE" in value or value.startswith("BIG") or value in ("INT8", "LONGLONG")
128
+ ):
129
+ sz = 64
130
+ elif "MEDIUM" in value or value in ("INT4", "UINT4", "LONG", "SERIAL"):
131
+ sz = 32
132
+ elif "SMALL" in value or value in ("INT2", "UINT2", "SHORT"):
133
+ sz = 16
134
+ elif "TINY" in value:
135
+ sz = 8
136
+ elif n := re.sub(r"^\D+", "", value):
137
+ if (sz := int(n)) <= 8:
138
+ sz = sz * 8
139
+ else:
140
+ sz = None
141
+
142
+ sz = modifier if (not sz and modifier) else sz
143
+ if not isinstance(sz, int):
144
+ sz = int(sz) if isinstance(sz, str) and sz.isdigit() else None
145
+ if (
146
+ ("U" in value and "MEDIUM" not in value)
147
+ or ("UNSIGNED" in value)
148
+ or value == "ROWID"
149
+ ):
150
+ dtype = integer_dtype_from_nbits(sz, unsigned=True, default=UInt64)
151
+ else:
152
+ dtype = integer_dtype_from_nbits(sz, unsigned=False, default=Int64)
153
+
154
+ # number types (note: 'number' alone is not that helpful and requires refinement)
155
+ elif "NUMBER" in value and "CARDINAL" in value:
156
+ dtype = UInt64
157
+
158
+ # decimal dtypes
159
+ elif (is_dec := ("DECIMAL" in value)) or ("NUMERIC" in value):
160
+ if "," in modifier:
161
+ prec, scale = modifier.split(",")
162
+ dtype = Decimal(int(prec), int(scale))
163
+ else:
164
+ dtype = Decimal if is_dec else Float64
165
+
166
+ # string dtypes
167
+ elif (
168
+ any(tp in value for tp in ("VARCHAR", "STRING", "TEXT", "UNICODE"))
169
+ or value.startswith(("STR", "CHAR", "BPCHAR", "NCHAR", "UTF"))
170
+ or value.endswith(("_UTF8", "_UTF16", "_UTF32"))
171
+ ):
172
+ dtype = String
173
+
174
+ # binary dtypes
175
+ elif value in ("BYTEA", "BYTES", "BLOB", "CLOB", "BINARY"):
176
+ dtype = Binary
177
+
178
+ # boolean dtypes
179
+ elif value.startswith("BOOL"):
180
+ dtype = Boolean
181
+
182
+ # null dtype; odd, but valid
183
+ elif value == "NULL":
184
+ dtype = Null
185
+
186
+ # temporal dtypes
187
+ elif value.startswith(("DATETIME", "TIMESTAMP")) and not (value.endswith("[D]")):
188
+ if any((tz in value.replace(" ", "")) for tz in ("TZ", "TIMEZONE")):
189
+ if "WITHOUT" not in value:
190
+ return None # there's a timezone, but we don't know what it is
191
+ unit = timeunit_from_precision(modifier) if modifier else "us"
192
+ dtype = Datetime(time_unit=(unit or "us")) # type: ignore[arg-type]
193
+ else:
194
+ value = re.sub(r"\d", "", value)
195
+ if value in ("INTERVAL", "TIMEDELTA", "DURATION"):
196
+ dtype = Duration
197
+ elif value == "DATE":
198
+ dtype = Date
199
+ elif value == "TIME":
200
+ dtype = Time
201
+
202
+ if not dtype and raise_unmatched:
203
+ msg = f"cannot infer dtype from {original_value!r} string value"
204
+ raise ValueError(msg)
205
+
206
+ return dtype
207
+
208
+
209
+ def dtype_from_cursor_description(
210
+ cursor: Any,
211
+ description: tuple[Any, ...],
212
+ ) -> PolarsDataType | None:
213
+ """Attempt to infer Polars dtype from database cursor description `type_code`."""
214
+ type_code, _disp_size, internal_size, precision, scale, *_ = description
215
+ dtype: PolarsDataType | None = None
216
+
217
+ if isclass(type_code):
218
+ # python types, eg: int, float, str, etc
219
+ with suppress(TypeError):
220
+ dtype = parse_py_type_into_dtype(type_code) # type: ignore[arg-type]
221
+
222
+ elif isinstance(type_code, str):
223
+ # database/sql type names, eg: "VARCHAR", "NUMERIC", "BLOB", etc
224
+ dtype = dtype_from_database_typename(
225
+ value=type_code,
226
+ raise_unmatched=False,
227
+ )
228
+
229
+ # check additional cursor attrs to refine dtype specification
230
+ if dtype is not None:
231
+ if dtype == Float64 and internal_size == 4:
232
+ dtype = Float32
233
+
234
+ elif dtype in INTEGER_DTYPES and internal_size in (2, 4, 8):
235
+ bits = internal_size * 8
236
+ dtype = integer_dtype_from_nbits(
237
+ bits,
238
+ unsigned=(dtype in UNSIGNED_INTEGER_DTYPES),
239
+ default=dtype,
240
+ )
241
+ elif (
242
+ dtype == Decimal
243
+ and isinstance(precision, int)
244
+ and isinstance(scale, int)
245
+ and precision <= 38
246
+ and scale <= 38
247
+ ):
248
+ dtype = Decimal(precision, scale)
249
+
250
+ return dtype
251
+
252
+
253
+ @functools.lru_cache(8)
254
+ def integer_dtype_from_nbits(
255
+ bits: int,
256
+ *,
257
+ unsigned: bool,
258
+ default: PolarsDataType | None = None,
259
+ ) -> PolarsDataType | None:
260
+ """
261
+ Return matching Polars integer dtype from num bits and signed/unsigned flag.
262
+
263
+ Examples
264
+ --------
265
+ >>> integer_dtype_from_nbits(8, unsigned=False)
266
+ Int8
267
+ >>> integer_dtype_from_nbits(32, unsigned=True)
268
+ UInt32
269
+ """
270
+ dtype = {
271
+ (8, False): Int8,
272
+ (8, True): UInt8,
273
+ (16, False): Int16,
274
+ (16, True): UInt16,
275
+ (32, False): Int32,
276
+ (32, True): UInt32,
277
+ (64, False): Int64,
278
+ (64, True): UInt64,
279
+ (128, False): Int128,
280
+ (128, True): Int128, # UInt128 not (yet?) supported
281
+ }.get((bits, unsigned), None)
282
+
283
+ if dtype is None and default is not None:
284
+ return default
285
+ return dtype
286
+
287
+
288
+ def timeunit_from_precision(precision: int | str | None) -> str | None:
289
+ """
290
+ Return `time_unit` from integer precision value.
291
+
292
+ Examples
293
+ --------
294
+ >>> timeunit_from_precision(3)
295
+ 'ms'
296
+ >>> timeunit_from_precision(5)
297
+ 'us'
298
+ >>> timeunit_from_precision(7)
299
+ 'ns'
300
+ """
301
+ from math import ceil
302
+
303
+ if not precision:
304
+ return None
305
+ elif isinstance(precision, str):
306
+ if precision.isdigit():
307
+ precision = int(precision)
308
+ elif (precision := precision.lower()) in ("s", "ms", "us", "ns"):
309
+ return "ms" if precision == "s" else precision
310
+ try:
311
+ n = min(max(3, int(ceil(precision / 3)) * 3), 9) # type: ignore[operator]
312
+ return {3: "ms", 6: "us", 9: "ns"}.get(n)
313
+ except TypeError:
314
+ return None
@@ -0,0 +1,144 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from importlib import import_module
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from polars._dependencies import import_optional
8
+ from polars._utils.various import parse_version
9
+ from polars.convert import from_arrow
10
+ from polars.exceptions import ModuleUpgradeRequiredError
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Coroutine
14
+
15
+ from polars import DataFrame
16
+ from polars._typing import SchemaDict
17
+
18
+
19
+ def _run_async(co: Coroutine[Any, Any, Any]) -> Any:
20
+ """Run asynchronous code as if it was synchronous."""
21
+ import asyncio
22
+
23
+ import polars._utils.nest_asyncio
24
+
25
+ polars._utils.nest_asyncio.apply() # type: ignore[attr-defined]
26
+ return asyncio.run(co)
27
+
28
+
29
+ def _read_sql_connectorx(
30
+ query: str | list[str],
31
+ connection_uri: str,
32
+ partition_on: str | None = None,
33
+ partition_range: tuple[int, int] | None = None,
34
+ partition_num: int | None = None,
35
+ protocol: str | None = None,
36
+ schema_overrides: SchemaDict | None = None,
37
+ pre_execution_query: str | list[str] | None = None,
38
+ ) -> DataFrame:
39
+ cx = import_optional("connectorx")
40
+
41
+ if parse_version(cx.__version__) < (0, 4, 2):
42
+ if pre_execution_query:
43
+ msg = "'pre_execution_query' is only supported in connectorx version 0.4.2 or later"
44
+ raise ValueError(msg)
45
+ return_type = "arrow2"
46
+ pre_execution_args = {}
47
+ else:
48
+ return_type = "arrow"
49
+ pre_execution_args = {"pre_execution_query": pre_execution_query}
50
+
51
+ try:
52
+ tbl = cx.read_sql(
53
+ conn=connection_uri,
54
+ query=query,
55
+ return_type=return_type,
56
+ partition_on=partition_on,
57
+ partition_range=partition_range,
58
+ partition_num=partition_num,
59
+ protocol=protocol,
60
+ **pre_execution_args,
61
+ )
62
+ except BaseException as err:
63
+ # basic sanitisation of /user:pass/ credentials exposed in connectorx errs
64
+ errmsg = re.sub("://[^:]+:[^:]+@", "://***:***@", str(err))
65
+ raise type(err)(errmsg) from err
66
+
67
+ return from_arrow(tbl, schema_overrides=schema_overrides) # type: ignore[return-value]
68
+
69
+
70
+ def _read_sql_adbc(
71
+ query: str,
72
+ connection_uri: str,
73
+ schema_overrides: SchemaDict | None,
74
+ execute_options: dict[str, Any] | None = None,
75
+ ) -> DataFrame:
76
+ with _open_adbc_connection(connection_uri) as conn, conn.cursor() as cursor:
77
+ cursor.execute(query, **(execute_options or {}))
78
+ tbl = cursor.fetch_arrow_table()
79
+ return from_arrow(tbl, schema_overrides=schema_overrides) # type: ignore[return-value]
80
+
81
+
82
+ def _get_adbc_driver_name_from_uri(connection_uri: str) -> str:
83
+ driver_name = connection_uri.split(":", 1)[0].lower()
84
+ # map uri prefix to ADBC name when not 1:1
85
+ driver_suffix_map: dict[str, str] = {"postgres": "postgresql"}
86
+ return driver_suffix_map.get(driver_name, driver_name)
87
+
88
+
89
+ def _get_adbc_module_name_from_uri(connection_uri: str) -> str:
90
+ driver_name = _get_adbc_driver_name_from_uri(connection_uri)
91
+ return f"adbc_driver_{driver_name}"
92
+
93
+
94
+ def _import_optional_adbc_driver(
95
+ module_name: str,
96
+ *,
97
+ dbapi_submodule: bool = True,
98
+ ) -> Any:
99
+ # Always import top level module first. This will surface a better error for users
100
+ # if the module does not exist. It doesn't negatively impact performance given the
101
+ # dbapi submodule would also load it.
102
+ adbc_driver = import_optional(
103
+ module_name,
104
+ err_prefix="ADBC",
105
+ err_suffix="driver not detected",
106
+ install_message=(
107
+ "If ADBC supports this database, please run: pip install "
108
+ f"{module_name.replace('_', '-')}"
109
+ ),
110
+ )
111
+ if not dbapi_submodule:
112
+ return adbc_driver
113
+ # Importing the dbapi without pyarrow before adbc_driver_manager 1.6.0
114
+ # raises ImportError: PyArrow is required for the DBAPI-compatible interface
115
+ # Use importlib.import_module because Polars' import_optional clobbers this error
116
+ try:
117
+ adbc_driver_dbapi = import_module(f"{module_name}.dbapi")
118
+ except ImportError as e:
119
+ if "PyArrow is required for the DBAPI-compatible interface" in (str(e)):
120
+ adbc_driver_manager = import_optional("adbc_driver_manager")
121
+ adbc_str_version = getattr(adbc_driver_manager, "__version__", "0.0")
122
+
123
+ msg = (
124
+ "pyarrow is required for adbc-driver-manager < 1.6.0, found "
125
+ f"{adbc_str_version}.\nEither upgrade `adbc-driver-manager` (suggested) or "
126
+ "install `pyarrow`"
127
+ )
128
+ raise ModuleUpgradeRequiredError(msg) from None
129
+ # if the error message was something different, re-raise it
130
+ raise
131
+ else:
132
+ return adbc_driver_dbapi
133
+
134
+
135
+ def _open_adbc_connection(connection_uri: str) -> Any:
136
+ driver_name = _get_adbc_driver_name_from_uri(connection_uri)
137
+ module_name = _get_adbc_module_name_from_uri(connection_uri)
138
+ adbc_driver = _import_optional_adbc_driver(module_name)
139
+
140
+ # some backends require the driver name to be stripped from the URI
141
+ if driver_name in ("duckdb", "snowflake", "sqlite"):
142
+ connection_uri = re.sub(f"^{driver_name}:/{{,3}}", "", connection_uri)
143
+
144
+ return adbc_driver.connect(connection_uri)