-
Notifications
You must be signed in to change notification settings - Fork 469
WIP: first pass at UnboundTransform
#209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -20,7 +20,7 @@ | |||||
| from abc import ABC, abstractmethod | ||||||
| from enum import IntEnum | ||||||
| from functools import singledispatch | ||||||
| from typing import Any, Callable, Generic, Optional, TypeVar | ||||||
| from typing import Any, Callable, Generic, Optional, Type, TypeVar | ||||||
| from typing import Literal as LiteralType | ||||||
| from uuid import UUID | ||||||
|
|
||||||
|
|
@@ -38,6 +38,7 @@ | |||||
| BoundNotIn, | ||||||
| BoundNotStartsWith, | ||||||
| BoundPredicate, | ||||||
| BoundReference, | ||||||
| BoundSetPredicate, | ||||||
| BoundStartsWith, | ||||||
| BoundTerm, | ||||||
|
|
@@ -49,6 +50,7 @@ | |||||
| Reference, | ||||||
| StartsWith, | ||||||
| UnboundPredicate, | ||||||
| UnboundTerm, | ||||||
| ) | ||||||
| from pyiceberg.expressions.literals import ( | ||||||
| DateLiteral, | ||||||
|
|
@@ -58,7 +60,8 @@ | |||||
| TimestampLiteral, | ||||||
| literal, | ||||||
| ) | ||||||
| from pyiceberg.typedef import IcebergRootModel, L | ||||||
| from pyiceberg.schema import Schema | ||||||
| from pyiceberg.typedef import IcebergRootModel, L, StructProtocol | ||||||
| from pyiceberg.types import ( | ||||||
| BinaryType, | ||||||
| DateType, | ||||||
|
|
@@ -821,3 +824,34 @@ class BoundTransform(BoundTerm[L]): | |||||
| def __init__(self, term: BoundTerm[L], transform: Transform[L, Any]): | ||||||
| self.term: BoundTerm[L] = term | ||||||
| self.transform = transform | ||||||
|
|
||||||
| def eval(self, struct: StructProtocol) -> L: | ||||||
| """Return the value at the referenced field's position in an object that abides by the StructProtocol.""" | ||||||
| return self.term.eval(struct) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| def ref(self) -> BoundReference[L]: | ||||||
| """Return the bound reference.""" | ||||||
| return self.term.ref() | ||||||
|
|
||||||
|
|
||||||
| class UnboundTransform(UnboundTerm[L]): | ||||||
| """An unbound transform expression.""" | ||||||
|
|
||||||
| transform: Transform[L, Any] | ||||||
|
|
||||||
| def __init__(self, term: UnboundTerm[L], transform: Transform[L, Any]): | ||||||
| self.term: UnboundTerm[L] = term | ||||||
| self.transform = transform | ||||||
|
|
||||||
| def bind(self, schema: Schema, case_sensitive: bool = True) -> BoundTransform[L]: | ||||||
| bound_term = self.term.bind(schema, case_sensitive) | ||||||
|
|
||||||
| if not self.transform.can_transform(bound_term.ref().field.field_type): | ||||||
| raise ValueError("some better error message") | ||||||
|
|
||||||
| else: | ||||||
| return BoundTransform(bound_term, self.transform) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should actually instantiate the callable of the transform:
Suggested change
|
||||||
|
|
||||||
| @property | ||||||
| def as_bound(self) -> Type[BoundTerm[L]]: | ||||||
| return BoundTerm[L] | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ | |
| StartsWith, | ||
| parser, | ||
| ) | ||
| from pyiceberg.transforms import Reference | ||
|
|
||
|
|
||
| def test_true() -> None: | ||
|
|
@@ -199,3 +200,8 @@ def test_with_function() -> None: | |
| parser.parse("foo = 1 and lower(bar) = '2'") | ||
|
|
||
| assert "Expected end of text, found 'and'" in str(exc_info) | ||
|
|
||
|
|
||
| def test_cast() -> None: | ||
| cast = parser.parse("CAST(created_at as date)") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would love to see some more tests here. The most important part that's missing here is converting |
||
| assert cast.term == Reference("created_at") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.