This repository was archived by the owner on Apr 1, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 68
chore: refactor IsNullOp and NotNullOp logic to make scalar ops generation easier #1822
Merged
Merged
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
30e1eb6
Refactor IsNullOp and NotNullOp logic
google-labs-jules[bot] 92870fd
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast 1b711aa
fix circular imports
tswast 7ede976
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast 9c17725
bad merge
tswast 78e4585
fix local pytest
tswast 333f9e2
Merge branch 'main' into refactor-isnull-op
tswast 53e0a3e
dont construct polars compiler if no polars
tswast 65e9fd4
Merge remote-tracking branch 'origin/refactor-isnull-op' into refacto…
tswast 360edb3
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast 9cd1fde
limit scope to just splitting large files
tswast ec47c37
Update bigframes/core/compile/compiled.py
tswast a475b72
Merge branch 'main' into refactor-isnull-op
tswast 5990732
Merge branch 'main' into refactor-isnull-op
tswast 8adafdd
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast b1cf81c
revert unneeded circular import workaround
tswast 57c1c98
Merge branch 'main' into refactor-isnull-op
tswast de5a12c
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast 87fd788
combine null ops into generic_ops files
tswast 6372a23
revert expression change
tswast ef06d65
Update bigframes/core/compile/polars/operations/__init__.py
tswast 7c19d8b
skip polars test for old polars
tswast 3d50dae
Merge remote-tracking branch 'origin/refactor-isnull-op' into refacto…
tswast 7babc87
Update bigframes/core/compile/ibis_compiler/operations/__init__.py
tswast aa5c47d
add minversion to skips
tswast b56cbbd
Merge remote-tracking branch 'origin/refactor-isnull-op' into refacto…
tswast 0c9802d
more skips
tswast a9152ba
Merge remote-tracking branch 'origin/main' into refactor-isnull-op
tswast c2f1ca8
fix minimum polars version detection
tswast 863b8ed
update colab constraints
tswast f42800e
skip polars on 3.10
tswast File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
bigframes/core/compile/ibis_compiler/operations/generic_ops.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Copyright 2025 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ | ||
| BigFrames -> Ibis compilation for the operations in bigframes.operations.generic_ops. | ||
|
|
||
| Please keep implementations in sequential order by op name. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from bigframes_vendored.ibis.expr import types as ibis_types | ||
|
|
||
| from bigframes.core.compile.ibis_compiler import scalar_op_compiler | ||
| from bigframes.operations import generic_ops | ||
|
|
||
| register_unary_op = scalar_op_compiler.scalar_op_compiler.register_unary_op | ||
|
|
||
|
|
||
| @register_unary_op(generic_ops.notnull_op) | ||
| def notnull_op_impl(x: ibis_types.Value): | ||
| return x.notnull() | ||
|
|
||
|
|
||
| @register_unary_op(generic_ops.isnull_op) | ||
| def isnull_op_impl(x: ibis_types.Value): | ||
| return x.isnull() |
207 changes: 207 additions & 0 deletions
207
bigframes/core/compile/ibis_compiler/scalar_op_compiler.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| # Copyright 2023 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """To avoid circular imports, this module should _not_ depend on any ops.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import functools | ||
| import typing | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| import bigframes_vendored.ibis.expr.types as ibis_types | ||
|
|
||
| import bigframes.core.compile.ibis_types | ||
| import bigframes.core.expression as ex | ||
|
|
||
| if TYPE_CHECKING: | ||
| import bigframes.operations as ops | ||
|
|
||
|
|
||
| class ScalarOpCompiler: | ||
| # Mapping of operation name to implemenations | ||
| _registry: dict[ | ||
| str, | ||
| typing.Callable[ | ||
| [typing.Sequence[ibis_types.Value], ops.RowOp], ibis_types.Value | ||
| ], | ||
| ] = {} | ||
|
|
||
| @functools.singledispatchmethod | ||
| def compile_expression( | ||
| self, | ||
| expression: ex.Expression, | ||
| bindings: typing.Dict[str, ibis_types.Value], | ||
| ) -> ibis_types.Value: | ||
| raise NotImplementedError(f"Unrecognized expression: {expression}") | ||
|
|
||
| @compile_expression.register | ||
| def _( | ||
| self, | ||
| expression: ex.ScalarConstantExpression, | ||
| bindings: typing.Dict[str, ibis_types.Value], | ||
| ) -> ibis_types.Value: | ||
| return bigframes.core.compile.ibis_types.literal_to_ibis_scalar( | ||
| expression.value, expression.dtype | ||
| ) | ||
|
|
||
| @compile_expression.register | ||
| def _( | ||
| self, | ||
| expression: ex.DerefOp, | ||
| bindings: typing.Dict[str, ibis_types.Value], | ||
| ) -> ibis_types.Value: | ||
| if expression.id.sql not in bindings: | ||
| raise ValueError(f"Could not resolve unbound variable {expression.id}") | ||
| else: | ||
| return bindings[expression.id.sql] | ||
|
|
||
| @compile_expression.register | ||
| def _( | ||
| self, | ||
| expression: ex.OpExpression, | ||
| bindings: typing.Dict[str, ibis_types.Value], | ||
| ) -> ibis_types.Value: | ||
| inputs = [ | ||
| self.compile_expression(sub_expr, bindings) | ||
| for sub_expr in expression.inputs | ||
| ] | ||
| return self.compile_row_op(expression.op, inputs) | ||
|
|
||
| def compile_row_op( | ||
| self, op: ops.RowOp, inputs: typing.Sequence[ibis_types.Value] | ||
| ) -> ibis_types.Value: | ||
| impl = self._registry[op.name] | ||
| return impl(inputs, op) | ||
|
|
||
| def register_unary_op( | ||
| self, | ||
| op_ref: typing.Union[ops.UnaryOp, type[ops.UnaryOp]], | ||
| pass_op: bool = False, | ||
| ): | ||
| """ | ||
| Decorator to register a unary op implementation. | ||
|
|
||
| Args: | ||
| op_ref (UnaryOp or UnaryOp type): | ||
| Class or instance of operator that is implemented by the decorated function. | ||
| pass_op (bool): | ||
| Set to true if implementation takes the operator object as the last argument. | ||
| This is needed for parameterized ops where parameters are part of op object. | ||
| """ | ||
| key = typing.cast(str, op_ref.name) | ||
|
|
||
| def decorator(impl: typing.Callable[..., ibis_types.Value]): | ||
| def normalized_impl(args: typing.Sequence[ibis_types.Value], op: ops.RowOp): | ||
| if pass_op: | ||
| return impl(args[0], op) | ||
| else: | ||
| return impl(args[0]) | ||
|
|
||
| self._register(key, normalized_impl) | ||
| return impl | ||
|
|
||
| return decorator | ||
|
|
||
| def register_binary_op( | ||
| self, | ||
| op_ref: typing.Union[ops.BinaryOp, type[ops.BinaryOp]], | ||
| pass_op: bool = False, | ||
| ): | ||
| """ | ||
| Decorator to register a binary op implementation. | ||
|
|
||
| Args: | ||
| op_ref (BinaryOp or BinaryOp type): | ||
| Class or instance of operator that is implemented by the decorated function. | ||
| pass_op (bool): | ||
| Set to true if implementation takes the operator object as the last argument. | ||
| This is needed for parameterized ops where parameters are part of op object. | ||
| """ | ||
| key = typing.cast(str, op_ref.name) | ||
|
|
||
| def decorator(impl: typing.Callable[..., ibis_types.Value]): | ||
| def normalized_impl(args: typing.Sequence[ibis_types.Value], op: ops.RowOp): | ||
| if pass_op: | ||
| return impl(args[0], args[1], op) | ||
| else: | ||
| return impl(args[0], args[1]) | ||
|
|
||
| self._register(key, normalized_impl) | ||
| return impl | ||
|
|
||
| return decorator | ||
|
|
||
| def register_ternary_op( | ||
| self, op_ref: typing.Union[ops.TernaryOp, type[ops.TernaryOp]] | ||
| ): | ||
| """ | ||
| Decorator to register a ternary op implementation. | ||
|
|
||
| Args: | ||
| op_ref (TernaryOp or TernaryOp type): | ||
| Class or instance of operator that is implemented by the decorated function. | ||
| """ | ||
| key = typing.cast(str, op_ref.name) | ||
|
|
||
| def decorator(impl: typing.Callable[..., ibis_types.Value]): | ||
| def normalized_impl(args: typing.Sequence[ibis_types.Value], op: ops.RowOp): | ||
| return impl(args[0], args[1], args[2]) | ||
|
|
||
| self._register(key, normalized_impl) | ||
| return impl | ||
|
|
||
| return decorator | ||
|
|
||
| def register_nary_op( | ||
| self, op_ref: typing.Union[ops.NaryOp, type[ops.NaryOp]], pass_op: bool = False | ||
| ): | ||
| """ | ||
| Decorator to register a nary op implementation. | ||
|
|
||
| Args: | ||
| op_ref (NaryOp or NaryOp type): | ||
| Class or instance of operator that is implemented by the decorated function. | ||
| pass_op (bool): | ||
| Set to true if implementation takes the operator object as the last argument. | ||
| This is needed for parameterized ops where parameters are part of op object. | ||
| """ | ||
| key = typing.cast(str, op_ref.name) | ||
|
|
||
| def decorator(impl: typing.Callable[..., ibis_types.Value]): | ||
| def normalized_impl(args: typing.Sequence[ibis_types.Value], op: ops.RowOp): | ||
| if pass_op: | ||
| return impl(*args, op=op) | ||
| else: | ||
| return impl(*args) | ||
|
|
||
| self._register(key, normalized_impl) | ||
| return impl | ||
|
|
||
| return decorator | ||
|
|
||
| def _register( | ||
| self, | ||
| op_name: str, | ||
| impl: typing.Callable[ | ||
| [typing.Sequence[ibis_types.Value], ops.RowOp], ibis_types.Value | ||
| ], | ||
| ): | ||
| if op_name in self._registry: | ||
| raise ValueError(f"Operation name {op_name} already registered") | ||
| self._registry[op_name] = impl | ||
|
|
||
|
|
||
| # Singleton compiler | ||
| scalar_op_compiler = ScalarOpCompiler() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.