duckdb 1.5.0.dev37__cp314-cp314t-macosx_10_13_universal2.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 duckdb might be problematic. Click here for more details.

Files changed (47) hide show
  1. _duckdb.cpython-314t-darwin.so +0 -0
  2. duckdb/__init__.py +475 -0
  3. duckdb/__init__.pyi +713 -0
  4. duckdb/bytes_io_wrapper.py +66 -0
  5. duckdb/experimental/__init__.py +2 -0
  6. duckdb/experimental/spark/LICENSE +260 -0
  7. duckdb/experimental/spark/__init__.py +7 -0
  8. duckdb/experimental/spark/_globals.py +77 -0
  9. duckdb/experimental/spark/_typing.py +48 -0
  10. duckdb/experimental/spark/conf.py +45 -0
  11. duckdb/experimental/spark/context.py +164 -0
  12. duckdb/experimental/spark/errors/__init__.py +72 -0
  13. duckdb/experimental/spark/errors/error_classes.py +918 -0
  14. duckdb/experimental/spark/errors/exceptions/__init__.py +16 -0
  15. duckdb/experimental/spark/errors/exceptions/base.py +217 -0
  16. duckdb/experimental/spark/errors/utils.py +116 -0
  17. duckdb/experimental/spark/exception.py +15 -0
  18. duckdb/experimental/spark/sql/__init__.py +7 -0
  19. duckdb/experimental/spark/sql/_typing.py +93 -0
  20. duckdb/experimental/spark/sql/catalog.py +78 -0
  21. duckdb/experimental/spark/sql/column.py +368 -0
  22. duckdb/experimental/spark/sql/conf.py +23 -0
  23. duckdb/experimental/spark/sql/dataframe.py +1437 -0
  24. duckdb/experimental/spark/sql/functions.py +6221 -0
  25. duckdb/experimental/spark/sql/group.py +420 -0
  26. duckdb/experimental/spark/sql/readwriter.py +449 -0
  27. duckdb/experimental/spark/sql/session.py +292 -0
  28. duckdb/experimental/spark/sql/streaming.py +37 -0
  29. duckdb/experimental/spark/sql/type_utils.py +105 -0
  30. duckdb/experimental/spark/sql/types.py +1275 -0
  31. duckdb/experimental/spark/sql/udf.py +37 -0
  32. duckdb/filesystem.py +23 -0
  33. duckdb/functional/__init__.py +17 -0
  34. duckdb/functional/__init__.pyi +31 -0
  35. duckdb/polars_io.py +237 -0
  36. duckdb/query_graph/__main__.py +363 -0
  37. duckdb/typing/__init__.py +61 -0
  38. duckdb/typing/__init__.pyi +36 -0
  39. duckdb/udf.py +19 -0
  40. duckdb/value/__init__.py +0 -0
  41. duckdb/value/__init__.pyi +0 -0
  42. duckdb/value/constant/__init__.py +268 -0
  43. duckdb/value/constant/__init__.pyi +115 -0
  44. duckdb-1.5.0.dev37.dist-info/METADATA +80 -0
  45. duckdb-1.5.0.dev37.dist-info/RECORD +47 -0
  46. duckdb-1.5.0.dev37.dist-info/WHEEL +6 -0
  47. duckdb-1.5.0.dev37.dist-info/licenses/LICENSE +7 -0
@@ -0,0 +1,1275 @@
1
+ # This code is based on code from Apache Spark under the license found in the LICENSE file located in the 'spark' folder.
2
+
3
+ from typing import (
4
+ cast,
5
+ overload,
6
+ Dict,
7
+ Optional,
8
+ List,
9
+ Tuple,
10
+ Any,
11
+ Union,
12
+ Type,
13
+ TypeVar,
14
+ ClassVar,
15
+ Iterator,
16
+ )
17
+ from builtins import tuple
18
+ import datetime
19
+ import calendar
20
+ import time
21
+ import math
22
+ import re
23
+
24
+ import duckdb
25
+ from duckdb.typing import DuckDBPyType
26
+
27
+ from ..exception import ContributionsAcceptedError
28
+
29
+ T = TypeVar("T")
30
+ U = TypeVar("U")
31
+
32
+ __all__ = [
33
+ "DataType",
34
+ "NullType",
35
+ "StringType",
36
+ "BinaryType",
37
+ "UUIDType",
38
+ "BitstringType",
39
+ "BooleanType",
40
+ "DateType",
41
+ "TimestampType",
42
+ "TimestampNTZType",
43
+ "TimestampNanosecondNTZType",
44
+ "TimestampMilisecondNTZType",
45
+ "TimestampSecondNTZType",
46
+ "TimeType",
47
+ "TimeNTZType",
48
+ "DecimalType",
49
+ "DoubleType",
50
+ "FloatType",
51
+ "ByteType",
52
+ "UnsignedByteType",
53
+ "ShortType",
54
+ "UnsignedShortType",
55
+ "IntegerType",
56
+ "UnsignedIntegerType",
57
+ "LongType",
58
+ "UnsignedLongType",
59
+ "HugeIntegerType",
60
+ "UnsignedHugeIntegerType",
61
+ "DayTimeIntervalType",
62
+ "Row",
63
+ "ArrayType",
64
+ "MapType",
65
+ "StructField",
66
+ "StructType",
67
+ ]
68
+
69
+
70
+ class DataType:
71
+ """Base class for data types."""
72
+
73
+ def __init__(self, duckdb_type):
74
+ self.duckdb_type = duckdb_type
75
+
76
+ def __repr__(self) -> str:
77
+ return self.__class__.__name__ + "()"
78
+
79
+ def __hash__(self) -> int:
80
+ return hash(str(self))
81
+
82
+ def __eq__(self, other: Any) -> bool:
83
+ return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
84
+
85
+ def __ne__(self, other: Any) -> bool:
86
+ return not self.__eq__(other)
87
+
88
+ @classmethod
89
+ def typeName(cls) -> str:
90
+ return cls.__name__[:-4].lower()
91
+
92
+ def simpleString(self) -> str:
93
+ return self.typeName()
94
+
95
+ def jsonValue(self) -> Union[str, Dict[str, Any]]:
96
+ raise ContributionsAcceptedError
97
+
98
+ def json(self) -> str:
99
+ raise ContributionsAcceptedError
100
+
101
+ def needConversion(self) -> bool:
102
+ """
103
+ Does this type needs conversion between Python object and internal SQL object.
104
+
105
+ This is used to avoid the unnecessary conversion for ArrayType/MapType/StructType.
106
+ """
107
+ return False
108
+
109
+ def toInternal(self, obj: Any) -> Any:
110
+ """
111
+ Converts a Python object into an internal SQL object.
112
+ """
113
+ return obj
114
+
115
+ def fromInternal(self, obj: Any) -> Any:
116
+ """
117
+ Converts an internal SQL object into a native Python object.
118
+ """
119
+ return obj
120
+
121
+
122
+ # This singleton pattern does not work with pickle, you will get
123
+ # another object after pickle and unpickle
124
+ class DataTypeSingleton(type):
125
+ """Metaclass for DataType"""
126
+
127
+ _instances: ClassVar[Dict[Type["DataTypeSingleton"], "DataTypeSingleton"]] = {}
128
+
129
+ def __call__(cls: Type[T]) -> T: # type: ignore[override]
130
+ if cls not in cls._instances: # type: ignore[attr-defined]
131
+ cls._instances[cls] = super(DataTypeSingleton, cls).__call__() # type: ignore[misc, attr-defined]
132
+ return cls._instances[cls] # type: ignore[attr-defined]
133
+
134
+
135
+ class NullType(DataType, metaclass=DataTypeSingleton):
136
+ """Null type.
137
+
138
+ The data type representing None, used for the types that cannot be inferred.
139
+ """
140
+
141
+ def __init__(self):
142
+ super().__init__(DuckDBPyType("NULL"))
143
+
144
+ @classmethod
145
+ def typeName(cls) -> str:
146
+ return "void"
147
+
148
+
149
+ class AtomicType(DataType):
150
+ """An internal type used to represent everything that is not
151
+ null, UDTs, arrays, structs, and maps."""
152
+
153
+
154
+ class NumericType(AtomicType):
155
+ """Numeric data types."""
156
+
157
+
158
+ class IntegralType(NumericType, metaclass=DataTypeSingleton):
159
+ """Integral data types."""
160
+
161
+
162
+ class FractionalType(NumericType):
163
+ """Fractional data types."""
164
+
165
+
166
+ class StringType(AtomicType, metaclass=DataTypeSingleton):
167
+ """String data type."""
168
+
169
+ def __init__(self):
170
+ super().__init__(DuckDBPyType("VARCHAR"))
171
+
172
+
173
+ class BitstringType(AtomicType, metaclass=DataTypeSingleton):
174
+ """Bitstring data type."""
175
+
176
+ def __init__(self):
177
+ super().__init__(DuckDBPyType("BIT"))
178
+
179
+
180
+ class UUIDType(AtomicType, metaclass=DataTypeSingleton):
181
+ """UUID data type."""
182
+
183
+ def __init__(self):
184
+ super().__init__(DuckDBPyType("UUID"))
185
+
186
+
187
+ class BinaryType(AtomicType, metaclass=DataTypeSingleton):
188
+ """Binary (byte array) data type."""
189
+
190
+ def __init__(self):
191
+ super().__init__(DuckDBPyType("BLOB"))
192
+
193
+
194
+ class BooleanType(AtomicType, metaclass=DataTypeSingleton):
195
+ """Boolean data type."""
196
+
197
+ def __init__(self):
198
+ super().__init__(DuckDBPyType("BOOLEAN"))
199
+
200
+
201
+ class DateType(AtomicType, metaclass=DataTypeSingleton):
202
+ """Date (datetime.date) data type."""
203
+
204
+ def __init__(self):
205
+ super().__init__(DuckDBPyType("DATE"))
206
+
207
+ EPOCH_ORDINAL = datetime.datetime(1970, 1, 1).toordinal()
208
+
209
+ def needConversion(self) -> bool:
210
+ return True
211
+
212
+ def toInternal(self, d: datetime.date) -> int:
213
+ if d is not None:
214
+ return d.toordinal() - self.EPOCH_ORDINAL
215
+
216
+ def fromInternal(self, v: int) -> datetime.date:
217
+ if v is not None:
218
+ return datetime.date.fromordinal(v + self.EPOCH_ORDINAL)
219
+
220
+
221
+ class TimestampType(AtomicType, metaclass=DataTypeSingleton):
222
+ """Timestamp (datetime.datetime) data type."""
223
+
224
+ def __init__(self):
225
+ super().__init__(DuckDBPyType("TIMESTAMPTZ"))
226
+
227
+ @classmethod
228
+ def typeName(cls) -> str:
229
+ return "timestamptz"
230
+
231
+ def needConversion(self) -> bool:
232
+ return True
233
+
234
+ def toInternal(self, dt: datetime.datetime) -> int:
235
+ if dt is not None:
236
+ seconds = calendar.timegm(dt.utctimetuple()) if dt.tzinfo else time.mktime(dt.timetuple())
237
+ return int(seconds) * 1000000 + dt.microsecond
238
+
239
+ def fromInternal(self, ts: int) -> datetime.datetime:
240
+ if ts is not None:
241
+ # using int to avoid precision loss in float
242
+ return datetime.datetime.fromtimestamp(ts // 1000000).replace(microsecond=ts % 1000000)
243
+
244
+
245
+ class TimestampNTZType(AtomicType, metaclass=DataTypeSingleton):
246
+ """Timestamp (datetime.datetime) data type without timezone information with microsecond precision."""
247
+
248
+ def __init__(self):
249
+ super().__init__(DuckDBPyType("TIMESTAMP"))
250
+
251
+ def needConversion(self) -> bool:
252
+ return True
253
+
254
+ @classmethod
255
+ def typeName(cls) -> str:
256
+ return "timestamp"
257
+
258
+ def toInternal(self, dt: datetime.datetime) -> int:
259
+ if dt is not None:
260
+ seconds = calendar.timegm(dt.timetuple())
261
+ return int(seconds) * 1000000 + dt.microsecond
262
+
263
+ def fromInternal(self, ts: int) -> datetime.datetime:
264
+ if ts is not None:
265
+ # using int to avoid precision loss in float
266
+ return datetime.datetime.utcfromtimestamp(ts // 1000000).replace(microsecond=ts % 1000000)
267
+
268
+
269
+ class TimestampSecondNTZType(AtomicType, metaclass=DataTypeSingleton):
270
+ """Timestamp (datetime.datetime) data type without timezone information with second precision."""
271
+
272
+ def __init__(self):
273
+ super().__init__(DuckDBPyType("TIMESTAMP_S"))
274
+
275
+ def needConversion(self) -> bool:
276
+ return True
277
+
278
+ @classmethod
279
+ def typeName(cls) -> str:
280
+ return "timestamp_s"
281
+
282
+ def toInternal(self, dt: datetime.datetime) -> int:
283
+ raise ContributionsAcceptedError
284
+
285
+ def fromInternal(self, ts: int) -> datetime.datetime:
286
+ raise ContributionsAcceptedError
287
+
288
+
289
+ class TimestampMilisecondNTZType(AtomicType, metaclass=DataTypeSingleton):
290
+ """Timestamp (datetime.datetime) data type without timezone information with milisecond precision."""
291
+
292
+ def __init__(self):
293
+ super().__init__(DuckDBPyType("TIMESTAMP_MS"))
294
+
295
+ def needConversion(self) -> bool:
296
+ return True
297
+
298
+ @classmethod
299
+ def typeName(cls) -> str:
300
+ return "timestamp_ms"
301
+
302
+ def toInternal(self, dt: datetime.datetime) -> int:
303
+ raise ContributionsAcceptedError
304
+
305
+ def fromInternal(self, ts: int) -> datetime.datetime:
306
+ raise ContributionsAcceptedError
307
+
308
+
309
+ class TimestampNanosecondNTZType(AtomicType, metaclass=DataTypeSingleton):
310
+ """Timestamp (datetime.datetime) data type without timezone information with nanosecond precision."""
311
+
312
+ def __init__(self):
313
+ super().__init__(DuckDBPyType("TIMESTAMP_NS"))
314
+
315
+ def needConversion(self) -> bool:
316
+ return True
317
+
318
+ @classmethod
319
+ def typeName(cls) -> str:
320
+ return "timestamp_ns"
321
+
322
+ def toInternal(self, dt: datetime.datetime) -> int:
323
+ raise ContributionsAcceptedError
324
+
325
+ def fromInternal(self, ts: int) -> datetime.datetime:
326
+ raise ContributionsAcceptedError
327
+
328
+
329
+ class DecimalType(FractionalType):
330
+ """Decimal (decimal.Decimal) data type.
331
+
332
+ The DecimalType must have fixed precision (the maximum total number of digits)
333
+ and scale (the number of digits on the right of dot). For example, (5, 2) can
334
+ support the value from [-999.99 to 999.99].
335
+
336
+ The precision can be up to 38, the scale must be less or equal to precision.
337
+
338
+ When creating a DecimalType, the default precision and scale is (10, 0). When inferring
339
+ schema from decimal.Decimal objects, it will be DecimalType(38, 18).
340
+
341
+ Parameters
342
+ ----------
343
+ precision : int, optional
344
+ the maximum (i.e. total) number of digits (default: 10)
345
+ scale : int, optional
346
+ the number of digits on right side of dot. (default: 0)
347
+ """
348
+
349
+ def __init__(self, precision: int = 10, scale: int = 0):
350
+ super().__init__(duckdb.decimal_type(precision, scale))
351
+ self.precision = precision
352
+ self.scale = scale
353
+ self.hasPrecisionInfo = True # this is a public API
354
+
355
+ def simpleString(self) -> str:
356
+ return "decimal(%d,%d)" % (self.precision, self.scale)
357
+
358
+ def __repr__(self) -> str:
359
+ return "DecimalType(%d,%d)" % (self.precision, self.scale)
360
+
361
+
362
+ class DoubleType(FractionalType, metaclass=DataTypeSingleton):
363
+ """Double data type, representing double precision floats."""
364
+
365
+ def __init__(self):
366
+ super().__init__(DuckDBPyType("DOUBLE"))
367
+
368
+
369
+ class FloatType(FractionalType, metaclass=DataTypeSingleton):
370
+ """Float data type, representing single precision floats."""
371
+
372
+ def __init__(self):
373
+ super().__init__(DuckDBPyType("FLOAT"))
374
+
375
+
376
+ class ByteType(IntegralType):
377
+ """Byte data type, i.e. a signed integer in a single byte."""
378
+
379
+ def __init__(self):
380
+ super().__init__(DuckDBPyType("TINYINT"))
381
+
382
+ def simpleString(self) -> str:
383
+ return "tinyint"
384
+
385
+
386
+ class UnsignedByteType(IntegralType):
387
+ """Unsigned byte data type, i.e. a unsigned integer in a single byte."""
388
+
389
+ def __init__(self):
390
+ super().__init__(DuckDBPyType("UTINYINT"))
391
+
392
+ def simpleString(self) -> str:
393
+ return "utinyint"
394
+
395
+
396
+ class ShortType(IntegralType):
397
+ """Short data type, i.e. a signed 16-bit integer."""
398
+
399
+ def __init__(self):
400
+ super().__init__(DuckDBPyType("SMALLINT"))
401
+
402
+ def simpleString(self) -> str:
403
+ return "smallint"
404
+
405
+
406
+ class UnsignedShortType(IntegralType):
407
+ """Unsigned short data type, i.e. a unsigned 16-bit integer."""
408
+
409
+ def __init__(self):
410
+ super().__init__(DuckDBPyType("USMALLINT"))
411
+
412
+ def simpleString(self) -> str:
413
+ return "usmallint"
414
+
415
+
416
+ class IntegerType(IntegralType):
417
+ """Int data type, i.e. a signed 32-bit integer."""
418
+
419
+ def __init__(self):
420
+ super().__init__(DuckDBPyType("INTEGER"))
421
+
422
+ def simpleString(self) -> str:
423
+ return "integer"
424
+
425
+
426
+ class UnsignedIntegerType(IntegralType):
427
+ """Unsigned int data type, i.e. a unsigned 32-bit integer."""
428
+
429
+ def __init__(self):
430
+ super().__init__(DuckDBPyType("UINTEGER"))
431
+
432
+ def simpleString(self) -> str:
433
+ return "uinteger"
434
+
435
+
436
+ class LongType(IntegralType):
437
+ """Long data type, i.e. a signed 64-bit integer.
438
+
439
+ If the values are beyond the range of [-9223372036854775808, 9223372036854775807],
440
+ please use :class:`DecimalType`.
441
+ """
442
+
443
+ def __init__(self):
444
+ super().__init__(DuckDBPyType("BIGINT"))
445
+
446
+ def simpleString(self) -> str:
447
+ return "bigint"
448
+
449
+
450
+ class UnsignedLongType(IntegralType):
451
+ """Unsigned long data type, i.e. a unsigned 64-bit integer.
452
+
453
+ If the values are beyond the range of [0, 18446744073709551615],
454
+ please use :class:`HugeIntegerType`.
455
+ """
456
+
457
+ def __init__(self):
458
+ super().__init__(DuckDBPyType("UBIGINT"))
459
+
460
+ def simpleString(self) -> str:
461
+ return "ubigint"
462
+
463
+
464
+ class HugeIntegerType(IntegralType):
465
+ """Huge integer data type, i.e. a signed 128-bit integer.
466
+
467
+ If the values are beyond the range of [-170141183460469231731687303715884105728, 170141183460469231731687303715884105727],
468
+ please use :class:`DecimalType`.
469
+ """
470
+
471
+ def __init__(self):
472
+ super().__init__(DuckDBPyType("HUGEINT"))
473
+
474
+ def simpleString(self) -> str:
475
+ return "hugeint"
476
+
477
+
478
+ class UnsignedHugeIntegerType(IntegralType):
479
+ """Unsigned huge integer data type, i.e. a unsigned 128-bit integer.
480
+
481
+ If the values are beyond the range of [0, 340282366920938463463374607431768211455],
482
+ please use :class:`DecimalType`.
483
+ """
484
+
485
+ def __init__(self):
486
+ super().__init__(DuckDBPyType("UHUGEINT"))
487
+
488
+ def simpleString(self) -> str:
489
+ return "uhugeint"
490
+
491
+
492
+ class TimeType(IntegralType):
493
+ """Time (datetime.time) data type."""
494
+
495
+ def __init__(self):
496
+ super().__init__(DuckDBPyType("TIMETZ"))
497
+
498
+ def simpleString(self) -> str:
499
+ return "timetz"
500
+
501
+
502
+ class TimeNTZType(IntegralType):
503
+ """Time (datetime.time) data type without timezone information."""
504
+
505
+ def __init__(self):
506
+ super().__init__(DuckDBPyType("TIME"))
507
+
508
+ def simpleString(self) -> str:
509
+ return "time"
510
+
511
+
512
+ class DayTimeIntervalType(AtomicType):
513
+ """DayTimeIntervalType (datetime.timedelta)."""
514
+
515
+ DAY = 0
516
+ HOUR = 1
517
+ MINUTE = 2
518
+ SECOND = 3
519
+
520
+ _fields = {
521
+ DAY: "day",
522
+ HOUR: "hour",
523
+ MINUTE: "minute",
524
+ SECOND: "second",
525
+ }
526
+
527
+ _inverted_fields = dict(zip(_fields.values(), _fields.keys()))
528
+
529
+ def __init__(self, startField: Optional[int] = None, endField: Optional[int] = None):
530
+ super().__init__(DuckDBPyType("INTERVAL"))
531
+ if startField is None and endField is None:
532
+ # Default matched to scala side.
533
+ startField = DayTimeIntervalType.DAY
534
+ endField = DayTimeIntervalType.SECOND
535
+ elif startField is not None and endField is None:
536
+ endField = startField
537
+
538
+ fields = DayTimeIntervalType._fields
539
+ if startField not in fields.keys() or endField not in fields.keys():
540
+ raise RuntimeError("interval %s to %s is invalid" % (startField, endField))
541
+ self.startField = cast(int, startField)
542
+ self.endField = cast(int, endField)
543
+
544
+ def _str_repr(self) -> str:
545
+ fields = DayTimeIntervalType._fields
546
+ start_field_name = fields[self.startField]
547
+ end_field_name = fields[self.endField]
548
+ if start_field_name == end_field_name:
549
+ return "interval %s" % start_field_name
550
+ else:
551
+ return "interval %s to %s" % (start_field_name, end_field_name)
552
+
553
+ simpleString = _str_repr
554
+
555
+ def __repr__(self) -> str:
556
+ return "%s(%d, %d)" % (type(self).__name__, self.startField, self.endField)
557
+
558
+ def needConversion(self) -> bool:
559
+ return True
560
+
561
+ def toInternal(self, dt: datetime.timedelta) -> Optional[int]:
562
+ if dt is not None:
563
+ return (math.floor(dt.total_seconds()) * 1000000) + dt.microseconds
564
+
565
+ def fromInternal(self, micros: int) -> Optional[datetime.timedelta]:
566
+ if micros is not None:
567
+ return datetime.timedelta(microseconds=micros)
568
+
569
+
570
+ class ArrayType(DataType):
571
+ """Array data type.
572
+
573
+ Parameters
574
+ ----------
575
+ elementType : :class:`DataType`
576
+ :class:`DataType` of each element in the array.
577
+ containsNull : bool, optional
578
+ whether the array can contain null (None) values.
579
+
580
+ Examples
581
+ --------
582
+ >>> ArrayType(StringType()) == ArrayType(StringType(), True)
583
+ True
584
+ >>> ArrayType(StringType(), False) == ArrayType(StringType())
585
+ False
586
+ """
587
+
588
+ def __init__(self, elementType: DataType, containsNull: bool = True):
589
+ super().__init__(duckdb.list_type(elementType.duckdb_type))
590
+ assert isinstance(elementType, DataType), "elementType %s should be an instance of %s" % (
591
+ elementType,
592
+ DataType,
593
+ )
594
+ self.elementType = elementType
595
+ self.containsNull = containsNull
596
+
597
+ def simpleString(self) -> str:
598
+ return "array<%s>" % self.elementType.simpleString()
599
+
600
+ def __repr__(self) -> str:
601
+ return "ArrayType(%s, %s)" % (self.elementType, str(self.containsNull))
602
+
603
+ def needConversion(self) -> bool:
604
+ return self.elementType.needConversion()
605
+
606
+ def toInternal(self, obj: List[Optional[T]]) -> List[Optional[T]]:
607
+ if not self.needConversion():
608
+ return obj
609
+ return obj and [self.elementType.toInternal(v) for v in obj]
610
+
611
+ def fromInternal(self, obj: List[Optional[T]]) -> List[Optional[T]]:
612
+ if not self.needConversion():
613
+ return obj
614
+ return obj and [self.elementType.fromInternal(v) for v in obj]
615
+
616
+
617
+ class MapType(DataType):
618
+ """Map data type.
619
+
620
+ Parameters
621
+ ----------
622
+ keyType : :class:`DataType`
623
+ :class:`DataType` of the keys in the map.
624
+ valueType : :class:`DataType`
625
+ :class:`DataType` of the values in the map.
626
+ valueContainsNull : bool, optional
627
+ indicates whether values can contain null (None) values.
628
+
629
+ Notes
630
+ -----
631
+ Keys in a map data type are not allowed to be null (None).
632
+
633
+ Examples
634
+ --------
635
+ >>> (MapType(StringType(), IntegerType())
636
+ ... == MapType(StringType(), IntegerType(), True))
637
+ True
638
+ >>> (MapType(StringType(), IntegerType(), False)
639
+ ... == MapType(StringType(), FloatType()))
640
+ False
641
+ """
642
+
643
+ def __init__(self, keyType: DataType, valueType: DataType, valueContainsNull: bool = True):
644
+ super().__init__(duckdb.map_type(keyType.duckdb_type, valueType.duckdb_type))
645
+ assert isinstance(keyType, DataType), "keyType %s should be an instance of %s" % (
646
+ keyType,
647
+ DataType,
648
+ )
649
+ assert isinstance(valueType, DataType), "valueType %s should be an instance of %s" % (
650
+ valueType,
651
+ DataType,
652
+ )
653
+ self.keyType = keyType
654
+ self.valueType = valueType
655
+ self.valueContainsNull = valueContainsNull
656
+
657
+ def simpleString(self) -> str:
658
+ return "map<%s,%s>" % (
659
+ self.keyType.simpleString(),
660
+ self.valueType.simpleString(),
661
+ )
662
+
663
+ def __repr__(self) -> str:
664
+ return "MapType(%s, %s, %s)" % (
665
+ self.keyType,
666
+ self.valueType,
667
+ str(self.valueContainsNull),
668
+ )
669
+
670
+ def needConversion(self) -> bool:
671
+ return self.keyType.needConversion() or self.valueType.needConversion()
672
+
673
+ def toInternal(self, obj: Dict[T, Optional[U]]) -> Dict[T, Optional[U]]:
674
+ if not self.needConversion():
675
+ return obj
676
+ return obj and dict((self.keyType.toInternal(k), self.valueType.toInternal(v)) for k, v in obj.items())
677
+
678
+ def fromInternal(self, obj: Dict[T, Optional[U]]) -> Dict[T, Optional[U]]:
679
+ if not self.needConversion():
680
+ return obj
681
+ return obj and dict((self.keyType.fromInternal(k), self.valueType.fromInternal(v)) for k, v in obj.items())
682
+
683
+
684
+ class StructField(DataType):
685
+ """A field in :class:`StructType`.
686
+
687
+ Parameters
688
+ ----------
689
+ name : str
690
+ name of the field.
691
+ dataType : :class:`DataType`
692
+ :class:`DataType` of the field.
693
+ nullable : bool, optional
694
+ whether the field can be null (None) or not.
695
+ metadata : dict, optional
696
+ a dict from string to simple type that can be toInternald to JSON automatically
697
+
698
+ Examples
699
+ --------
700
+ >>> (StructField("f1", StringType(), True)
701
+ ... == StructField("f1", StringType(), True))
702
+ True
703
+ >>> (StructField("f1", StringType(), True)
704
+ ... == StructField("f2", StringType(), True))
705
+ False
706
+ """
707
+
708
+ def __init__(
709
+ self,
710
+ name: str,
711
+ dataType: DataType,
712
+ nullable: bool = True,
713
+ metadata: Optional[Dict[str, Any]] = None,
714
+ ):
715
+ super().__init__(dataType.duckdb_type)
716
+ assert isinstance(dataType, DataType), "dataType %s should be an instance of %s" % (
717
+ dataType,
718
+ DataType,
719
+ )
720
+ assert isinstance(name, str), "field name %s should be a string" % (name)
721
+ self.name = name
722
+ self.dataType = dataType
723
+ self.nullable = nullable
724
+ self.metadata = metadata or {}
725
+
726
+ def simpleString(self) -> str:
727
+ return "%s:%s" % (self.name, self.dataType.simpleString())
728
+
729
+ def __repr__(self) -> str:
730
+ return "StructField('%s', %s, %s)" % (
731
+ self.name,
732
+ self.dataType,
733
+ str(self.nullable),
734
+ )
735
+
736
+ def needConversion(self) -> bool:
737
+ return self.dataType.needConversion()
738
+
739
+ def toInternal(self, obj: T) -> T:
740
+ return self.dataType.toInternal(obj)
741
+
742
+ def fromInternal(self, obj: T) -> T:
743
+ return self.dataType.fromInternal(obj)
744
+
745
+ def typeName(self) -> str: # type: ignore[override]
746
+ raise TypeError("StructField does not have typeName. " "Use typeName on its type explicitly instead.")
747
+
748
+
749
+ class StructType(DataType):
750
+ """Struct type, consisting of a list of :class:`StructField`.
751
+
752
+ This is the data type representing a :class:`Row`.
753
+
754
+ Iterating a :class:`StructType` will iterate over its :class:`StructField`\\s.
755
+ A contained :class:`StructField` can be accessed by its name or position.
756
+
757
+ Examples
758
+ --------
759
+ >>> struct1 = StructType([StructField("f1", StringType(), True)])
760
+ >>> struct1["f1"]
761
+ StructField('f1', StringType(), True)
762
+ >>> struct1[0]
763
+ StructField('f1', StringType(), True)
764
+
765
+ >>> struct1 = StructType([StructField("f1", StringType(), True)])
766
+ >>> struct2 = StructType([StructField("f1", StringType(), True)])
767
+ >>> struct1 == struct2
768
+ True
769
+ >>> struct1 = StructType([StructField("f1", StringType(), True)])
770
+ >>> struct2 = StructType([StructField("f1", StringType(), True),
771
+ ... StructField("f2", IntegerType(), False)])
772
+ >>> struct1 == struct2
773
+ False
774
+ """
775
+
776
+ def _update_internal_duckdb_type(self):
777
+ self.duckdb_type = duckdb.struct_type(dict(zip(self.names, [x.duckdb_type for x in self.fields])))
778
+
779
+ def __init__(self, fields: Optional[List[StructField]] = None):
780
+ if not fields:
781
+ self.fields = []
782
+ self.names = []
783
+ else:
784
+ self.fields = fields
785
+ self.names = [f.name for f in fields]
786
+ assert all(isinstance(f, StructField) for f in fields), "fields should be a list of StructField"
787
+ # Precalculated list of fields that need conversion with fromInternal/toInternal functions
788
+ self._needConversion = [f.needConversion() for f in self]
789
+ self._needSerializeAnyField = any(self._needConversion)
790
+ super().__init__(duckdb.struct_type(dict(zip(self.names, [x.duckdb_type for x in self.fields]))))
791
+
792
+ @overload
793
+ def add(
794
+ self,
795
+ field: str,
796
+ data_type: Union[str, DataType],
797
+ nullable: bool = True,
798
+ metadata: Optional[Dict[str, Any]] = None,
799
+ ) -> "StructType":
800
+ ...
801
+
802
+ @overload
803
+ def add(self, field: StructField) -> "StructType":
804
+ ...
805
+
806
+ def add(
807
+ self,
808
+ field: Union[str, StructField],
809
+ data_type: Optional[Union[str, DataType]] = None,
810
+ nullable: bool = True,
811
+ metadata: Optional[Dict[str, Any]] = None,
812
+ ) -> "StructType":
813
+ """
814
+ Construct a :class:`StructType` by adding new elements to it, to define the schema.
815
+ The method accepts either:
816
+
817
+ a) A single parameter which is a :class:`StructField` object.
818
+ b) Between 2 and 4 parameters as (name, data_type, nullable (optional),
819
+ metadata(optional). The data_type parameter may be either a String or a
820
+ :class:`DataType` object.
821
+
822
+ Parameters
823
+ ----------
824
+ field : str or :class:`StructField`
825
+ Either the name of the field or a :class:`StructField` object
826
+ data_type : :class:`DataType`, optional
827
+ If present, the DataType of the :class:`StructField` to create
828
+ nullable : bool, optional
829
+ Whether the field to add should be nullable (default True)
830
+ metadata : dict, optional
831
+ Any additional metadata (default None)
832
+
833
+ Returns
834
+ -------
835
+ :class:`StructType`
836
+
837
+ Examples
838
+ --------
839
+ >>> struct1 = StructType().add("f1", StringType(), True).add("f2", StringType(), True, None)
840
+ >>> struct2 = StructType([StructField("f1", StringType(), True), \\
841
+ ... StructField("f2", StringType(), True, None)])
842
+ >>> struct1 == struct2
843
+ True
844
+ >>> struct1 = StructType().add(StructField("f1", StringType(), True))
845
+ >>> struct2 = StructType([StructField("f1", StringType(), True)])
846
+ >>> struct1 == struct2
847
+ True
848
+ >>> struct1 = StructType().add("f1", "string", True)
849
+ >>> struct2 = StructType([StructField("f1", StringType(), True)])
850
+ >>> struct1 == struct2
851
+ True
852
+ """
853
+ if isinstance(field, StructField):
854
+ self.fields.append(field)
855
+ self.names.append(field.name)
856
+ else:
857
+ if isinstance(field, str) and data_type is None:
858
+ raise ValueError("Must specify DataType if passing name of struct_field to create.")
859
+ else:
860
+ data_type_f = data_type
861
+ self.fields.append(StructField(field, data_type_f, nullable, metadata))
862
+ self.names.append(field)
863
+ # Precalculated list of fields that need conversion with fromInternal/toInternal functions
864
+ self._needConversion = [f.needConversion() for f in self]
865
+ self._needSerializeAnyField = any(self._needConversion)
866
+ self._update_internal_duckdb_type()
867
+ return self
868
+
869
+ def __iter__(self) -> Iterator[StructField]:
870
+ """Iterate the fields"""
871
+ return iter(self.fields)
872
+
873
+ def __len__(self) -> int:
874
+ """Return the number of fields."""
875
+ return len(self.fields)
876
+
877
+ def __getitem__(self, key: Union[str, int]) -> StructField:
878
+ """Access fields by name or slice."""
879
+ if isinstance(key, str):
880
+ for field in self:
881
+ if field.name == key:
882
+ return field
883
+ raise KeyError("No StructField named {0}".format(key))
884
+ elif isinstance(key, int):
885
+ try:
886
+ return self.fields[key]
887
+ except IndexError:
888
+ raise IndexError("StructType index out of range")
889
+ elif isinstance(key, slice):
890
+ return StructType(self.fields[key])
891
+ else:
892
+ raise TypeError("StructType keys should be strings, integers or slices")
893
+
894
+ def simpleString(self) -> str:
895
+ return "struct<%s>" % (",".join(f.simpleString() for f in self))
896
+
897
+ def __repr__(self) -> str:
898
+ return "StructType([%s])" % ", ".join(str(field) for field in self)
899
+
900
+ def __contains__(self, item: Any) -> bool:
901
+ return item in self.names
902
+
903
+ def extract_types_and_names(self) -> Tuple[List[str], List[str]]:
904
+ names = []
905
+ types = []
906
+ for f in self.fields:
907
+ types.append(str(f.dataType.duckdb_type))
908
+ names.append(f.name)
909
+ return (types, names)
910
+
911
+ def fieldNames(self) -> List[str]:
912
+ """
913
+ Returns all field names in a list.
914
+
915
+ Examples
916
+ --------
917
+ >>> struct = StructType([StructField("f1", StringType(), True)])
918
+ >>> struct.fieldNames()
919
+ ['f1']
920
+ """
921
+ return list(self.names)
922
+
923
+ def needConversion(self) -> bool:
924
+ # We need convert Row()/namedtuple into tuple()
925
+ return True
926
+
927
+ def toInternal(self, obj: Tuple) -> Tuple:
928
+ if obj is None:
929
+ return
930
+
931
+ if self._needSerializeAnyField:
932
+ # Only calling toInternal function for fields that need conversion
933
+ if isinstance(obj, dict):
934
+ return tuple(
935
+ f.toInternal(obj.get(n)) if c else obj.get(n)
936
+ for n, f, c in zip(self.names, self.fields, self._needConversion)
937
+ )
938
+ elif isinstance(obj, (tuple, list)):
939
+ return tuple(f.toInternal(v) if c else v for f, v, c in zip(self.fields, obj, self._needConversion))
940
+ elif hasattr(obj, "__dict__"):
941
+ d = obj.__dict__
942
+ return tuple(
943
+ f.toInternal(d.get(n)) if c else d.get(n)
944
+ for n, f, c in zip(self.names, self.fields, self._needConversion)
945
+ )
946
+ else:
947
+ raise ValueError("Unexpected tuple %r with StructType" % obj)
948
+ else:
949
+ if isinstance(obj, dict):
950
+ return tuple(obj.get(n) for n in self.names)
951
+ elif isinstance(obj, (list, tuple)):
952
+ return tuple(obj)
953
+ elif hasattr(obj, "__dict__"):
954
+ d = obj.__dict__
955
+ return tuple(d.get(n) for n in self.names)
956
+ else:
957
+ raise ValueError("Unexpected tuple %r with StructType" % obj)
958
+
959
+ def fromInternal(self, obj: Tuple) -> "Row":
960
+ if obj is None:
961
+ return
962
+ if isinstance(obj, Row):
963
+ # it's already converted by pickler
964
+ return obj
965
+
966
+ values: Union[Tuple, List]
967
+ if self._needSerializeAnyField:
968
+ # Only calling fromInternal function for fields that need conversion
969
+ values = [f.fromInternal(v) if c else v for f, v, c in zip(self.fields, obj, self._needConversion)]
970
+ else:
971
+ values = obj
972
+ return _create_row(self.names, values)
973
+
974
+
975
+ class UnionType(DataType):
976
+ def __init__(self):
977
+ raise ContributionsAcceptedError
978
+
979
+
980
+ class UserDefinedType(DataType):
981
+ """User-defined type (UDT).
982
+
983
+ .. note:: WARN: Spark Internal Use Only
984
+ """
985
+
986
+ def __init__(self):
987
+ raise ContributionsAcceptedError
988
+
989
+ @classmethod
990
+ def typeName(cls) -> str:
991
+ return cls.__name__.lower()
992
+
993
+ @classmethod
994
+ def sqlType(cls) -> DataType:
995
+ """
996
+ Underlying SQL storage type for this UDT.
997
+ """
998
+ raise NotImplementedError("UDT must implement sqlType().")
999
+
1000
+ @classmethod
1001
+ def module(cls) -> str:
1002
+ """
1003
+ The Python module of the UDT.
1004
+ """
1005
+ raise NotImplementedError("UDT must implement module().")
1006
+
1007
+ @classmethod
1008
+ def scalaUDT(cls) -> str:
1009
+ """
1010
+ The class name of the paired Scala UDT (could be '', if there
1011
+ is no corresponding one).
1012
+ """
1013
+ return ""
1014
+
1015
+ def needConversion(self) -> bool:
1016
+ return True
1017
+
1018
+ @classmethod
1019
+ def _cachedSqlType(cls) -> DataType:
1020
+ """
1021
+ Cache the sqlType() into class, because it's heavily used in `toInternal`.
1022
+ """
1023
+ if not hasattr(cls, "_cached_sql_type"):
1024
+ cls._cached_sql_type = cls.sqlType() # type: ignore[attr-defined]
1025
+ return cls._cached_sql_type # type: ignore[attr-defined]
1026
+
1027
+ def toInternal(self, obj: Any) -> Any:
1028
+ if obj is not None:
1029
+ return self._cachedSqlType().toInternal(self.serialize(obj))
1030
+
1031
+ def fromInternal(self, obj: Any) -> Any:
1032
+ v = self._cachedSqlType().fromInternal(obj)
1033
+ if v is not None:
1034
+ return self.deserialize(v)
1035
+
1036
+ def serialize(self, obj: Any) -> Any:
1037
+ """
1038
+ Converts a user-type object into a SQL datum.
1039
+ """
1040
+ raise NotImplementedError("UDT must implement toInternal().")
1041
+
1042
+ def deserialize(self, datum: Any) -> Any:
1043
+ """
1044
+ Converts a SQL datum into a user-type object.
1045
+ """
1046
+ raise NotImplementedError("UDT must implement fromInternal().")
1047
+
1048
+ def simpleString(self) -> str:
1049
+ return "udt"
1050
+
1051
+ def __eq__(self, other: Any) -> bool:
1052
+ return type(self) == type(other)
1053
+
1054
+
1055
+ _atomic_types: List[Type[DataType]] = [
1056
+ StringType,
1057
+ BinaryType,
1058
+ BooleanType,
1059
+ DecimalType,
1060
+ FloatType,
1061
+ DoubleType,
1062
+ ByteType,
1063
+ ShortType,
1064
+ IntegerType,
1065
+ LongType,
1066
+ DateType,
1067
+ TimestampType,
1068
+ TimestampNTZType,
1069
+ NullType,
1070
+ ]
1071
+ _all_atomic_types: Dict[str, Type[DataType]] = dict((t.typeName(), t) for t in _atomic_types)
1072
+
1073
+ _complex_types: List[Type[Union[ArrayType, MapType, StructType]]] = [
1074
+ ArrayType,
1075
+ MapType,
1076
+ StructType,
1077
+ ]
1078
+ _all_complex_types: Dict[str, Type[Union[ArrayType, MapType, StructType]]] = dict(
1079
+ (v.typeName(), v) for v in _complex_types
1080
+ )
1081
+
1082
+
1083
+ _FIXED_DECIMAL = re.compile(r"decimal\(\s*(\d+)\s*,\s*(-?\d+)\s*\)")
1084
+ _INTERVAL_DAYTIME = re.compile(r"interval (day|hour|minute|second)( to (day|hour|minute|second))?")
1085
+
1086
+
1087
+ def _create_row(fields: Union["Row", List[str]], values: Union[Tuple[Any, ...], List[Any]]) -> "Row":
1088
+ row = Row(*values)
1089
+ row.__fields__ = fields
1090
+ return row
1091
+
1092
+
1093
+ class Row(tuple):
1094
+
1095
+ """
1096
+ A row in :class:`DataFrame`.
1097
+ The fields in it can be accessed:
1098
+
1099
+ * like attributes (``row.key``)
1100
+ * like dictionary values (``row[key]``)
1101
+
1102
+ ``key in row`` will search through row keys.
1103
+
1104
+ Row can be used to create a row object by using named arguments.
1105
+ It is not allowed to omit a named argument to represent that the value is
1106
+ None or missing. This should be explicitly set to None in this case.
1107
+
1108
+ .. versionchanged:: 3.0.0
1109
+ Rows created from named arguments no longer have
1110
+ field names sorted alphabetically and will be ordered in the position as
1111
+ entered.
1112
+
1113
+ Examples
1114
+ --------
1115
+ >>> row = Row(name="Alice", age=11)
1116
+ >>> row
1117
+ Row(name='Alice', age=11)
1118
+ >>> row['name'], row['age']
1119
+ ('Alice', 11)
1120
+ >>> row.name, row.age
1121
+ ('Alice', 11)
1122
+ >>> 'name' in row
1123
+ True
1124
+ >>> 'wrong_key' in row
1125
+ False
1126
+
1127
+ Row also can be used to create another Row like class, then it
1128
+ could be used to create Row objects, such as
1129
+
1130
+ >>> Person = Row("name", "age")
1131
+ >>> Person
1132
+ <Row('name', 'age')>
1133
+ >>> 'name' in Person
1134
+ True
1135
+ >>> 'wrong_key' in Person
1136
+ False
1137
+ >>> Person("Alice", 11)
1138
+ Row(name='Alice', age=11)
1139
+
1140
+ This form can also be used to create rows as tuple values, i.e. with unnamed
1141
+ fields.
1142
+
1143
+ >>> row1 = Row("Alice", 11)
1144
+ >>> row2 = Row(name="Alice", age=11)
1145
+ >>> row1 == row2
1146
+ True
1147
+ """
1148
+
1149
+ @overload
1150
+ def __new__(cls, *args: str) -> "Row":
1151
+ ...
1152
+
1153
+ @overload
1154
+ def __new__(cls, **kwargs: Any) -> "Row":
1155
+ ...
1156
+
1157
+ def __new__(cls, *args: Optional[str], **kwargs: Optional[Any]) -> "Row":
1158
+ if args and kwargs:
1159
+ raise ValueError("Can not use both args " "and kwargs to create Row")
1160
+ if kwargs:
1161
+ # create row objects
1162
+ row = tuple.__new__(cls, list(kwargs.values()))
1163
+ row.__fields__ = list(kwargs.keys())
1164
+ return row
1165
+ else:
1166
+ # create row class or objects
1167
+ return tuple.__new__(cls, args)
1168
+
1169
+ def asDict(self, recursive: bool = False) -> Dict[str, Any]:
1170
+ """
1171
+ Return as a dict
1172
+
1173
+ Parameters
1174
+ ----------
1175
+ recursive : bool, optional
1176
+ turns the nested Rows to dict (default: False).
1177
+
1178
+ Notes
1179
+ -----
1180
+ If a row contains duplicate field names, e.g., the rows of a join
1181
+ between two :class:`DataFrame` that both have the fields of same names,
1182
+ one of the duplicate fields will be selected by ``asDict``. ``__getitem__``
1183
+ will also return one of the duplicate fields, however returned value might
1184
+ be different to ``asDict``.
1185
+
1186
+ Examples
1187
+ --------
1188
+ >>> Row(name="Alice", age=11).asDict() == {'name': 'Alice', 'age': 11}
1189
+ True
1190
+ >>> row = Row(key=1, value=Row(name='a', age=2))
1191
+ >>> row.asDict() == {'key': 1, 'value': Row(name='a', age=2)}
1192
+ True
1193
+ >>> row.asDict(True) == {'key': 1, 'value': {'name': 'a', 'age': 2}}
1194
+ True
1195
+ """
1196
+ if not hasattr(self, "__fields__"):
1197
+ raise TypeError("Cannot convert a Row class into dict")
1198
+
1199
+ if recursive:
1200
+
1201
+ def conv(obj: Any) -> Any:
1202
+ if isinstance(obj, Row):
1203
+ return obj.asDict(True)
1204
+ elif isinstance(obj, list):
1205
+ return [conv(o) for o in obj]
1206
+ elif isinstance(obj, dict):
1207
+ return dict((k, conv(v)) for k, v in obj.items())
1208
+ else:
1209
+ return obj
1210
+
1211
+ return dict(zip(self.__fields__, (conv(o) for o in self)))
1212
+ else:
1213
+ return dict(zip(self.__fields__, self))
1214
+
1215
+ def __contains__(self, item: Any) -> bool:
1216
+ if hasattr(self, "__fields__"):
1217
+ return item in self.__fields__
1218
+ else:
1219
+ return super(Row, self).__contains__(item)
1220
+
1221
+ # let object acts like class
1222
+ def __call__(self, *args: Any) -> "Row":
1223
+ """create new Row object"""
1224
+ if len(args) > len(self):
1225
+ raise ValueError(
1226
+ "Can not create Row with fields %s, expected %d values " "but got %s" % (self, len(self), args)
1227
+ )
1228
+ return _create_row(self, args)
1229
+
1230
+ def __getitem__(self, item: Any) -> Any:
1231
+ if isinstance(item, (int, slice)):
1232
+ return super(Row, self).__getitem__(item)
1233
+ try:
1234
+ # it will be slow when it has many fields,
1235
+ # but this will not be used in normal cases
1236
+ idx = self.__fields__.index(item)
1237
+ return super(Row, self).__getitem__(idx)
1238
+ except IndexError:
1239
+ raise KeyError(item)
1240
+ except ValueError:
1241
+ raise ValueError(item)
1242
+
1243
+ def __getattr__(self, item: str) -> Any:
1244
+ if item.startswith("__"):
1245
+ raise AttributeError(item)
1246
+ try:
1247
+ # it will be slow when it has many fields,
1248
+ # but this will not be used in normal cases
1249
+ idx = self.__fields__.index(item)
1250
+ return self[idx]
1251
+ except IndexError:
1252
+ raise AttributeError(item)
1253
+ except ValueError:
1254
+ raise AttributeError(item)
1255
+
1256
+ def __setattr__(self, key: Any, value: Any) -> None:
1257
+ if key != "__fields__":
1258
+ raise RuntimeError("Row is read-only")
1259
+ self.__dict__[key] = value
1260
+
1261
+ def __reduce__(
1262
+ self,
1263
+ ) -> Union[str, Tuple[Any, ...]]:
1264
+ """Returns a tuple so Python knows how to pickle Row."""
1265
+ if hasattr(self, "__fields__"):
1266
+ return (_create_row, (self.__fields__, tuple(self)))
1267
+ else:
1268
+ return tuple.__reduce__(self)
1269
+
1270
+ def __repr__(self) -> str:
1271
+ """Printable representation of Row used in Python REPL."""
1272
+ if hasattr(self, "__fields__"):
1273
+ return "Row(%s)" % ", ".join("%s=%r" % (k, v) for k, v in zip(self.__fields__, tuple(self)))
1274
+ else:
1275
+ return "<Row(%s)>" % ", ".join("%r" % field for field in self)