Skip to content

Commit 03b0c24

Browse files
authored
Merge branch 'main' into b374307132-information_schema
2 parents 02aa7d4 + 24050cb commit 03b0c24

File tree

27 files changed

+1120
-34
lines changed

27 files changed

+1120
-34
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,8 @@ repos:
4242
additional_dependencies: [types-requests, types-tabulate, types-PyYAML, pandas-stubs<=2.2.3.241126]
4343
exclude: "^third_party"
4444
args: ["--check-untyped-defs", "--explicit-package-bases", "--ignore-missing-imports"]
45+
- repo: https://github.com/biomejs/pre-commit
46+
rev: v2.0.2
47+
hooks:
48+
- id: biome-check
49+
files: '\.js$'

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# Generated by synthtool. DO NOT EDIT!
1818
include README.rst LICENSE
1919
recursive-include third_party/bigframes_vendored *
20-
recursive-include bigframes *.json *.proto py.typed
20+
recursive-include bigframes *.json *.proto *.js py.typed
2121
recursive-include tests *
2222
global-exclude *.py[co]
2323
global-exclude __pycache__

bigframes/core/compile/polars/compiler.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,14 @@ def compile_offsets(self, node: nodes.PromoteOffsetsNode):
487487
def compile_join(self, node: nodes.JoinNode):
488488
left = self.compile_node(node.left_child)
489489
right = self.compile_node(node.right_child)
490-
left_on = [l_name.id.sql for l_name, _ in node.conditions]
491-
right_on = [r_name.id.sql for _, r_name in node.conditions]
490+
491+
left_on = []
492+
right_on = []
493+
for left_ex, right_ex in node.conditions:
494+
left_ex, right_ex = lowering._coerce_comparables(left_ex, right_ex)
495+
left_on.append(self.expr_compiler.compile_expression(left_ex))
496+
right_on.append(self.expr_compiler.compile_expression(right_ex))
497+
492498
if node.type == "right":
493499
return self._ordered_join(
494500
right, left, "left", right_on, left_on, node.joins_nulls
@@ -502,8 +508,8 @@ def _ordered_join(
502508
left_frame: pl.LazyFrame,
503509
right_frame: pl.LazyFrame,
504510
how: Literal["inner", "outer", "left", "cross"],
505-
left_on: Sequence[str],
506-
right_on: Sequence[str],
511+
left_on: Sequence[pl.Expr],
512+
right_on: Sequence[pl.Expr],
507513
join_nulls: bool,
508514
):
509515
if how == "right":

bigframes/core/indexes/base.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
from __future__ import annotations
1818

19+
import functools
1920
import typing
20-
from typing import Hashable, Literal, Optional, overload, Sequence, Union
21+
from typing import cast, Hashable, Literal, Optional, overload, Sequence, Union
2122

2223
import bigframes_vendored.constants as constants
2324
import bigframes_vendored.pandas.core.indexes.base as vendored_pandas_index
@@ -529,6 +530,29 @@ def isin(self, values) -> Index:
529530
)
530531
).fillna(value=False)
531532

533+
def __contains__(self, key) -> bool:
534+
hash(key) # to throw for unhashable values
535+
if self.nlevels == 0:
536+
return False
537+
538+
if (not isinstance(key, tuple)) or (self.nlevels == 1):
539+
key = (key,)
540+
541+
match_exprs = []
542+
for key_part, index_col, dtype in zip(
543+
key, self._block.index_columns, self._block.index.dtypes
544+
):
545+
key_type = bigframes.dtypes.is_compatible(key_part, dtype)
546+
if key_type is None:
547+
return False
548+
key_expr = ex.const(key_part, key_type)
549+
match_expr = ops.eq_null_match_op.as_expr(ex.deref(index_col), key_expr)
550+
match_exprs.append(match_expr)
551+
552+
match_expr_final = functools.reduce(ops.and_op.as_expr, match_exprs)
553+
block, match_col = self._block.project_expr(match_expr_final)
554+
return cast(bool, block.get_stat(match_col, agg_ops.AnyOp()))
555+
532556
def _apply_unary_expr(
533557
self,
534558
op: ex.Expression,

bigframes/core/tools/datetimes.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from collections.abc import Mapping
16-
from datetime import datetime
16+
from datetime import date, datetime
1717
from typing import Optional, Union
1818

1919
import bigframes_vendored.constants as constants
@@ -28,7 +28,7 @@
2828

2929
def to_datetime(
3030
arg: Union[
31-
Union[int, float, str, datetime],
31+
Union[int, float, str, datetime, date],
3232
vendored_pandas_datetimes.local_iterables,
3333
bigframes.series.Series,
3434
bigframes.dataframe.DataFrame,
@@ -38,7 +38,7 @@ def to_datetime(
3838
format: Optional[str] = None,
3939
unit: Optional[str] = None,
4040
) -> Union[pd.Timestamp, datetime, bigframes.series.Series]:
41-
if isinstance(arg, (int, float, str, datetime)):
41+
if isinstance(arg, (int, float, str, datetime, date)):
4242
return pd.to_datetime(
4343
arg,
4444
utc=utc,
@@ -62,7 +62,11 @@ def to_datetime(
6262
f"Unit parameter is not supported for non-numerical input types. {constants.FEEDBACK_LINK}"
6363
)
6464

65-
if arg.dtype in (bigframes.dtypes.TIMESTAMP_DTYPE, bigframes.dtypes.DATETIME_DTYPE):
65+
if arg.dtype in (
66+
bigframes.dtypes.TIMESTAMP_DTYPE,
67+
bigframes.dtypes.DATETIME_DTYPE,
68+
bigframes.dtypes.DATE_DTYPE,
69+
):
6670
to_type = (
6771
bigframes.dtypes.TIMESTAMP_DTYPE if utc else bigframes.dtypes.DATETIME_DTYPE
6872
)

bigframes/dataframe.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ def __len__(self):
374374
def __iter__(self):
375375
return iter(self.columns)
376376

377+
def __contains__(self, key) -> bool:
378+
return key in self.columns
379+
377380
def astype(
378381
self,
379382
dtype: Union[
@@ -779,22 +782,7 @@ def _repr_html_(self) -> str:
779782
if opts.repr_mode == "deferred":
780783
return formatter.repr_query_job(self._compute_dry_run())
781784

782-
if opts.repr_mode == "anywidget":
783-
import anywidget # type: ignore
784-
785-
# create an iterator for the data batches
786-
batches = self.to_pandas_batches()
787-
788-
# get the first page result
789-
try:
790-
first_page = next(iter(batches))
791-
except StopIteration:
792-
first_page = pandas.DataFrame(columns=self.columns)
793-
794-
# Instantiate and return the widget. The widget's frontend will
795-
# handle the display of the table and pagination
796-
return anywidget.AnyWidget(dataframe=first_page)
797-
785+
# Process blob columns first, regardless of display mode
798786
self._cached()
799787
df = self.copy()
800788
if bigframes.options.display.blob_display:
@@ -806,7 +794,31 @@ def _repr_html_(self) -> str:
806794
for col in blob_cols:
807795
# TODO(garrettwu): Not necessary to get access urls for all the rows. Update when having a to get URLs from local data.
808796
df[col] = df[col].blob._get_runtime(mode="R", with_metadata=True)
797+
else:
798+
blob_cols = []
809799

800+
if opts.repr_mode == "anywidget":
801+
try:
802+
from IPython.display import display as ipython_display
803+
804+
from bigframes import display
805+
806+
# Always create a new widget instance for each display call
807+
# This ensures that each cell gets its own widget and prevents
808+
# unintended sharing between cells
809+
widget = display.TableWidget(df.copy())
810+
811+
ipython_display(widget)
812+
return "" # Return empty string since we used display()
813+
814+
except (AttributeError, ValueError, ImportError):
815+
# Fallback if anywidget is not available
816+
warnings.warn(
817+
"Anywidget mode is not available. Please `pip install anywidget traitlets` or `pip install 'bigframes[anywidget]'` to use interactive tables. Falling back to deferred mode."
818+
)
819+
return formatter.repr_query_job(self._compute_dry_run())
820+
821+
# Continue with regular HTML rendering for non-anywidget modes
810822
# TODO(swast): pass max_columns and get the true column count back. Maybe
811823
# get 1 more column than we have requested so that pandas can add the
812824
# ... for us?
@@ -815,7 +827,6 @@ def _repr_html_(self) -> str:
815827
)
816828

817829
self._set_internal_query_job(query_job)
818-
819830
column_count = len(pandas_df.columns)
820831

821832
with display_options.pandas_repr(opts):

bigframes/display/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
try:
18+
import anywidget # noqa
19+
20+
from bigframes.display.anywidget import TableWidget
21+
22+
__all__ = ["TableWidget"]
23+
except Exception:
24+
pass

0 commit comments

Comments
 (0)