Skip to content

Commit bfdd9b2

Browse files
authored
Python: Implement project in Transform implementations (#6128)
1 parent 7190637 commit bfdd9b2

File tree

5 files changed

+701
-17
lines changed

5 files changed

+701
-17
lines changed

python/pyiceberg/expressions/__init__.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ def __eq__(self, other: Any) -> bool:
309309
return self.term == other.term
310310
return False
311311

312+
@property
313+
@abstractmethod
314+
def as_unbound(self) -> Type[UnboundPredicate[Any]]:
315+
...
316+
312317

313318
class UnboundPredicate(Generic[L], Unbound[BooleanExpression], BooleanExpression, ABC):
314319
term: UnboundTerm[Any]
@@ -347,6 +352,11 @@ class BoundUnaryPredicate(BoundPredicate[L], ABC):
347352
def __repr__(self) -> str:
348353
return f"{str(self.__class__.__name__)}(term={repr(self.term)})"
349354

355+
@property
356+
@abstractmethod
357+
def as_unbound(self) -> Type[UnaryPredicate]:
358+
...
359+
350360

351361
class BoundIsNull(BoundUnaryPredicate[L]):
352362
def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
@@ -357,6 +367,10 @@ def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
357367
def __invert__(self) -> BoundNotNull[L]:
358368
return BoundNotNull(self.term)
359369

370+
@property
371+
def as_unbound(self) -> Type[IsNull]:
372+
return IsNull
373+
360374

361375
class BoundNotNull(BoundUnaryPredicate[L]):
362376
def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
@@ -367,6 +381,10 @@ def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
367381
def __invert__(self) -> BoundIsNull[L]:
368382
return BoundIsNull(self.term)
369383

384+
@property
385+
def as_unbound(self) -> Type[NotNull]:
386+
return NotNull
387+
370388

371389
class IsNull(UnaryPredicate):
372390
def __invert__(self) -> NotNull:
@@ -396,6 +414,10 @@ def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
396414
def __invert__(self) -> BoundNotNaN[L]:
397415
return BoundNotNaN(self.term)
398416

417+
@property
418+
def as_unbound(self) -> Type[IsNaN]:
419+
return IsNaN
420+
399421

400422
class BoundNotNaN(BoundUnaryPredicate[L]):
401423
def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
@@ -407,6 +429,10 @@ def __new__(cls, term: BoundTerm[L]): # pylint: disable=W0221
407429
def __invert__(self) -> BoundIsNaN[L]:
408430
return BoundIsNaN(self.term)
409431

432+
@property
433+
def as_unbound(self) -> Type[NotNaN]:
434+
return NotNaN
435+
410436

411437
class IsNaN(UnaryPredicate):
412438
def __invert__(self) -> NotNaN:
@@ -477,6 +503,11 @@ def __repr__(self) -> str:
477503
def __eq__(self, other: Any) -> bool:
478504
return self.term == other.term and self.literals == other.literals if isinstance(other, BoundSetPredicate) else False
479505

506+
@property
507+
@abstractmethod
508+
def as_unbound(self) -> Type[SetPredicate[L]]:
509+
...
510+
480511

481512
class BoundIn(BoundSetPredicate[L]):
482513
def __new__(cls, term: BoundTerm[L], literals: Set[Literal[L]]): # pylint: disable=W0221
@@ -494,6 +525,10 @@ def __invert__(self) -> BoundNotIn[L]:
494525
def __eq__(self, other: Any) -> bool:
495526
return self.term == other.term and self.literals == other.literals if isinstance(other, BoundIn) else False
496527

528+
@property
529+
def as_unbound(self) -> Type[In[L]]:
530+
return In
531+
497532

498533
class BoundNotIn(BoundSetPredicate[L]):
499534
def __new__( # pylint: disable=W0221
@@ -512,6 +547,10 @@ def __new__( # pylint: disable=W0221
512547
def __invert__(self) -> BoundIn[L]:
513548
return BoundIn(self.term, self.literals)
514549

550+
@property
551+
def as_unbound(self) -> Type[NotIn[L]]:
552+
return NotIn
553+
515554

516555
class In(SetPredicate[L]):
517556
def __new__(
@@ -601,36 +640,65 @@ def __eq__(self, other):
601640
def __repr__(self) -> str:
602641
return f"{str(self.__class__.__name__)}(term={repr(self.term)}, literal={repr(self.literal)})"
603642

643+
@property
644+
@abstractmethod
645+
def as_unbound(self) -> Type[LiteralPredicate[L]]:
646+
...
647+
604648

605649
class BoundEqualTo(BoundLiteralPredicate[L]):
606650
def __invert__(self) -> BoundNotEqualTo[L]:
607651
return BoundNotEqualTo[L](self.term, self.literal)
608652

653+
@property
654+
def as_unbound(self) -> Type[EqualTo[L]]:
655+
return EqualTo
656+
609657

610658
class BoundNotEqualTo(BoundLiteralPredicate[L]):
611659
def __invert__(self) -> BoundEqualTo[L]:
612660
return BoundEqualTo[L](self.term, self.literal)
613661

662+
@property
663+
def as_unbound(self) -> Type[NotEqualTo[L]]:
664+
return NotEqualTo
665+
614666

615667
class BoundGreaterThanOrEqual(BoundLiteralPredicate[L]):
616668
def __invert__(self) -> BoundLessThan[L]:
617669
return BoundLessThan[L](self.term, self.literal)
618670

671+
@property
672+
def as_unbound(self) -> Type[GreaterThanOrEqual[L]]:
673+
return GreaterThanOrEqual[L]
674+
619675

620676
class BoundGreaterThan(BoundLiteralPredicate[L]):
621677
def __invert__(self) -> BoundLessThanOrEqual[L]:
622678
return BoundLessThanOrEqual(self.term, self.literal)
623679

680+
@property
681+
def as_unbound(self) -> Type[GreaterThan[L]]:
682+
return GreaterThan[L]
683+
624684

625685
class BoundLessThan(BoundLiteralPredicate[L]):
626686
def __invert__(self) -> BoundGreaterThanOrEqual[L]:
627687
return BoundGreaterThanOrEqual[L](self.term, self.literal)
628688

689+
@property
690+
def as_unbound(self) -> Type[LessThan[L]]:
691+
return LessThan[L]
692+
629693

630694
class BoundLessThanOrEqual(BoundLiteralPredicate[L]):
631695
def __invert__(self) -> BoundGreaterThan[L]:
632696
return BoundGreaterThan[L](self.term, self.literal)
633697

698+
@property
699+
def as_unbound(self) -> Type[LessThanOrEqual[L]]:
700+
return LessThanOrEqual[L]
701+
634702

635703
class EqualTo(LiteralPredicate[L]):
636704
def __invert__(self) -> NotEqualTo[L]:

python/pyiceberg/expressions/literals.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
timestamp_to_micros,
5454
timestamptz_to_micros,
5555
)
56+
from pyiceberg.utils.decimal import decimal_to_unscaled, unscaled_to_decimal
5657
from pyiceberg.utils.singleton import Singleton
5758

5859

@@ -210,6 +211,12 @@ def __init__(self, value: int):
210211
def to(self, type_var: IcebergType) -> Literal: # type: ignore
211212
raise TypeError(f"Cannot convert LongLiteral into {type_var}")
212213

214+
def increment(self) -> Literal[int]:
215+
return LongLiteral(self.value + 1)
216+
217+
def decrement(self) -> Literal[int]:
218+
return LongLiteral(self.value - 1)
219+
213220
@to.register(LongType)
214221
def _(self, _: LongType) -> Literal[int]:
215222
return self
@@ -319,6 +326,12 @@ class DateLiteral(Literal[int]):
319326
def __init__(self, value: int):
320327
super().__init__(value, int)
321328

329+
def increment(self) -> Literal[int]:
330+
return DateLiteral(self.value + 1)
331+
332+
def decrement(self) -> Literal[int]:
333+
return DateLiteral(self.value - 1)
334+
322335
@singledispatchmethod
323336
def to(self, type_var: IcebergType) -> Literal: # type: ignore
324337
raise TypeError(f"Cannot convert DateLiteral into {type_var}")
@@ -345,6 +358,12 @@ class TimestampLiteral(Literal[int]):
345358
def __init__(self, value: int):
346359
super().__init__(value, int)
347360

361+
def increment(self) -> Literal[int]:
362+
return TimestampLiteral(self.value + 1)
363+
364+
def decrement(self) -> Literal[int]:
365+
return TimestampLiteral(self.value - 1)
366+
348367
@singledispatchmethod
349368
def to(self, type_var: IcebergType) -> Literal: # type: ignore
350369
raise TypeError(f"Cannot convert TimestampLiteral into {type_var}")
@@ -362,6 +381,16 @@ class DecimalLiteral(Literal[Decimal]):
362381
def __init__(self, value: Decimal):
363382
super().__init__(value, Decimal)
364383

384+
def increment(self) -> Literal[Decimal]:
385+
original_scale = abs(self.value.as_tuple().exponent)
386+
unscaled = decimal_to_unscaled(self.value)
387+
return DecimalLiteral(unscaled_to_decimal(unscaled + 1, original_scale))
388+
389+
def decrement(self) -> Literal[Decimal]:
390+
original_scale = abs(self.value.as_tuple().exponent)
391+
unscaled = decimal_to_unscaled(self.value)
392+
return DecimalLiteral(unscaled_to_decimal(unscaled - 1, original_scale))
393+
365394
@singledispatchmethod
366395
def to(self, type_var: IcebergType) -> Literal: # type: ignore
367396
raise TypeError(f"Cannot convert DecimalLiteral into {type_var}")

0 commit comments

Comments
 (0)