Skip to content

Commit 49b431c

Browse files
authored
Merge branch 'main' into sycai_ai_gen_bool
2 parents 10e6497 + 21eb213 commit 49b431c

File tree

8 files changed

+96
-47
lines changed

8 files changed

+96
-47
lines changed

bigframes/core/log_adapter.py

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -149,49 +149,61 @@ def wrap(cls):
149149
return wrap(decorated_cls)
150150

151151

152-
def method_logger(method, /, *, custom_base_name: Optional[str] = None):
152+
def method_logger(method=None, /, *, custom_base_name: Optional[str] = None):
153153
"""Decorator that adds logging functionality to a method."""
154154

155-
@functools.wraps(method)
156-
def wrapper(*args, **kwargs):
157-
api_method_name = getattr(method, LOG_OVERRIDE_NAME, method.__name__)
158-
if custom_base_name is None:
159-
qualname_parts = getattr(method, "__qualname__", method.__name__).split(".")
160-
class_name = qualname_parts[-2] if len(qualname_parts) > 1 else ""
161-
base_name = (
162-
class_name if class_name else "_".join(method.__module__.split(".")[1:])
163-
)
164-
else:
165-
base_name = custom_base_name
166-
167-
full_method_name = f"{base_name.lower()}-{api_method_name}"
168-
# Track directly called methods
169-
if len(_call_stack) == 0:
170-
add_api_method(full_method_name)
171-
172-
_call_stack.append(full_method_name)
173-
174-
try:
175-
return method(*args, **kwargs)
176-
except (NotImplementedError, TypeError) as e:
177-
# Log method parameters that are implemented in pandas but either missing (TypeError)
178-
# or not fully supported (NotImplementedError) in BigFrames.
179-
# Logging is currently supported only when we can access the bqclient through
180-
# _block.session.bqclient.
181-
if len(_call_stack) == 1:
182-
submit_pandas_labels(
183-
_get_bq_client(*args, **kwargs),
184-
base_name,
185-
api_method_name,
186-
args,
187-
kwargs,
188-
task=PANDAS_PARAM_TRACKING_TASK,
155+
def outer_wrapper(method):
156+
@functools.wraps(method)
157+
def wrapper(*args, **kwargs):
158+
api_method_name = getattr(method, LOG_OVERRIDE_NAME, method.__name__)
159+
if custom_base_name is None:
160+
qualname_parts = getattr(method, "__qualname__", method.__name__).split(
161+
"."
162+
)
163+
class_name = qualname_parts[-2] if len(qualname_parts) > 1 else ""
164+
base_name = (
165+
class_name
166+
if class_name
167+
else "_".join(method.__module__.split(".")[1:])
189168
)
190-
raise e
191-
finally:
192-
_call_stack.pop()
169+
else:
170+
base_name = custom_base_name
193171

194-
return wrapper
172+
full_method_name = f"{base_name.lower()}-{api_method_name}"
173+
# Track directly called methods
174+
if len(_call_stack) == 0:
175+
add_api_method(full_method_name)
176+
177+
_call_stack.append(full_method_name)
178+
179+
try:
180+
return method(*args, **kwargs)
181+
except (NotImplementedError, TypeError) as e:
182+
# Log method parameters that are implemented in pandas but either missing (TypeError)
183+
# or not fully supported (NotImplementedError) in BigFrames.
184+
# Logging is currently supported only when we can access the bqclient through
185+
# _block.session.bqclient.
186+
if len(_call_stack) == 1:
187+
submit_pandas_labels(
188+
_get_bq_client(*args, **kwargs),
189+
base_name,
190+
api_method_name,
191+
args,
192+
kwargs,
193+
task=PANDAS_PARAM_TRACKING_TASK,
194+
)
195+
raise e
196+
finally:
197+
_call_stack.pop()
198+
199+
return wrapper
200+
201+
if method is None:
202+
# Called with parentheses
203+
return outer_wrapper
204+
205+
# Called without parentheses
206+
return outer_wrapper(method)
195207

196208

197209
def property_logger(prop):

bigframes/core/reshape/tile.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import bigframes_vendored.pandas.core.reshape.tile as vendored_pandas_tile
2121
import pandas as pd
2222

23+
import bigframes
2324
import bigframes.constants
2425
import bigframes.core.expression as ex
2526
import bigframes.core.ordering as order
@@ -32,7 +33,7 @@
3233

3334

3435
def cut(
35-
x: bigframes.series.Series,
36+
x,
3637
bins: typing.Union[
3738
int,
3839
pd.IntervalIndex,
@@ -60,9 +61,12 @@ def cut(
6061
f"but found {type(list(labels)[0])}. {constants.FEEDBACK_LINK}"
6162
)
6263

63-
if x.size == 0:
64+
if len(x) == 0:
6465
raise ValueError("Cannot cut empty array.")
6566

67+
if not isinstance(x, bigframes.series.Series):
68+
x = bigframes.series.Series(x)
69+
6670
if isinstance(bins, int):
6771
if bins <= 0:
6872
raise ValueError("`bins` should be a positive integer.")

bigframes/dataframe.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,9 +890,11 @@ def __delitem__(self, key: str):
890890
self._set_block(df._get_block())
891891

892892
def __setitem__(
893-
self, key: str | list[str], value: SingleItemValue | MultiItemValue
893+
self,
894+
key: str | list[str] | pandas.Index,
895+
value: SingleItemValue | MultiItemValue,
894896
):
895-
if isinstance(key, list):
897+
if isinstance(key, (list, pandas.Index)):
896898
df = self._assign_multi_items(key, value)
897899
else:
898900
df = self._assign_single_item(key, value)
@@ -2246,7 +2248,7 @@ def _assign_single_item(
22462248

22472249
def _assign_multi_items(
22482250
self,
2249-
k: list[str],
2251+
k: list[str] | pandas.Index,
22502252
v: SingleItemValue | MultiItemValue,
22512253
) -> DataFrame:
22522254
value_sources: Sequence[Any] = []

tests/system/small/test_dataframe.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,11 @@ def test_assign_new_column_w_setitem_list_error(scalars_dfs):
11971197
pytest.param(
11981198
["new_col", "new_col_too"], [1, 2], id="sequence_to_full_new_column"
11991199
),
1200+
pytest.param(
1201+
pd.Index(("new_col", "new_col_too")),
1202+
[1, 2],
1203+
id="sequence_to_full_new_column_as_index",
1204+
),
12001205
],
12011206
)
12021207
def test_setitem_multicolumn_with_literals(scalars_dfs, key, value):

tests/system/small/test_pandas.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,18 @@ def _convert_pandas_category(pd_s: pd.Series):
520520
)
521521

522522

523+
def test_cut_for_array():
524+
"""Avoid regressions for internal issue 329866195"""
525+
sc = [30, 80, 40, 90, 60, 45, 95, 75, 55, 100, 65, 85]
526+
x = [20, 40, 60, 80, 100]
527+
528+
pd_result: pd.Series = pd.Series(pd.cut(sc, x))
529+
bf_result = bpd.cut(sc, x)
530+
531+
pd_result = _convert_pandas_category(pd_result)
532+
pd.testing.assert_series_equal(bf_result.to_pandas(), pd_result)
533+
534+
523535
@pytest.mark.parametrize(
524536
("right", "labels"),
525537
[

tests/unit/core/test_log_adapter.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ def test_method_logging_with_custom_base_name(test_method_w_custom_base):
101101
assert "pandas-method1" in api_methods
102102

103103

104+
def test_method_logging_with_custom_base__logger_as_decorator():
105+
@log_adapter.method_logger(custom_base_name="pandas")
106+
def my_method():
107+
pass
108+
109+
my_method()
110+
111+
api_methods = log_adapter.get_and_reset_api_methods()
112+
assert "pandas-my_method" in api_methods
113+
114+
104115
def test_property_logging(test_instance):
105116
test_instance.my_field
106117

tests/unit/test_pandas.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def test_method_matches_session(method_name: str):
122122
)
123123
def test_cut_raises_with_invalid_labels(bins: int, labels, error_message: str):
124124
mock_series = mock.create_autospec(bigframes.pandas.Series, instance=True)
125+
mock_series.__len__.return_value = 5
125126
with pytest.raises(ValueError, match=error_message):
126127
bigframes.pandas.cut(mock_series, bins, labels=labels)
127128

@@ -160,6 +161,8 @@ def test_cut_raises_with_unsupported_labels():
160161
)
161162
def test_cut_raises_with_invalid_bins(bins: int, error_message: str):
162163
mock_series = mock.create_autospec(bigframes.pandas.Series, instance=True)
164+
mock_series.__len__.return_value = 5
165+
163166
with pytest.raises(ValueError, match=error_message):
164167
bigframes.pandas.cut(mock_series, bins, labels=False)
165168

third_party/bigframes_vendored/pandas/core/reshape/tile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88

99
import pandas as pd
1010

11-
from bigframes import constants, series
11+
from bigframes import constants
1212

1313

1414
def cut(
15-
x: series.Series,
15+
x,
1616
bins: typing.Union[
1717
int,
1818
pd.IntervalIndex,
@@ -113,7 +113,7 @@ def cut(
113113
dtype: struct<left_inclusive: int64, right_exclusive: int64>[pyarrow]
114114
115115
Args:
116-
x (bigframes.pandas.Series):
116+
x (array-like):
117117
The input Series to be binned. Must be 1-dimensional.
118118
bins (int, pd.IntervalIndex, Iterable):
119119
The criteria to bin by.

0 commit comments

Comments
 (0)