Skip to content

Commit 7b0cab5

Browse files
authored
chore: format warning message with newlines and ansi color (#1447)
* use ibis fill_null instead of fillna * minor typo in JSON warning messages * chore: format warning message with newlines and ansi color
1 parent 6716283 commit 7b0cab5

26 files changed

+160
-107
lines changed

bigframes/_config/bigquery_options.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ def _get_validated_location(value: Optional[str]) -> Optional[str]:
5959
# -> bpd.options.bigquery.location = "us-central-1"
6060
# -> location.setter
6161
# -> _get_validated_location
62-
msg = UNKNOWN_LOCATION_MESSAGE.format(location=location, possibility=possibility)
62+
msg = bfe.format_message(
63+
UNKNOWN_LOCATION_MESSAGE.format(location=location, possibility=possibility)
64+
)
6365
warnings.warn(msg, stacklevel=3, category=bfe.UnknownLocationWarning)
6466

6567
return value
@@ -294,7 +296,7 @@ def use_regional_endpoints(self, value: bool):
294296
)
295297

296298
if value:
297-
msg = (
299+
msg = bfe.format_message(
298300
"Use of regional endpoints is a feature in preview and "
299301
"available only in selected regions and projects. "
300302
)
@@ -354,7 +356,7 @@ def client_endpoints_override(self) -> dict:
354356

355357
@client_endpoints_override.setter
356358
def client_endpoints_override(self, value: dict):
357-
msg = (
359+
msg = bfe.format_message(
358360
"This is an advanced configuration option for directly setting endpoints. "
359361
"Incorrect use may lead to unexpected behavior or system instability. "
360362
"Proceed only if you fully understand its implications."

bigframes/_config/experiment_options.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def semantic_operators(self) -> bool:
3434
@semantic_operators.setter
3535
def semantic_operators(self, value: bool):
3636
if value is True:
37-
msg = (
37+
msg = bfe.format_message(
3838
"Semantic operators are still under experiments, and are subject "
3939
"to change in the future."
4040
)
@@ -48,7 +48,7 @@ def blob(self) -> bool:
4848
@blob.setter
4949
def blob(self, value: bool):
5050
if value is True:
51-
msg = (
51+
msg = bfe.format_message(
5252
"BigFrames Blob is still under experiments. It may not work and "
5353
"subject to change in the future."
5454
)
@@ -62,7 +62,7 @@ def udf(self) -> bool:
6262
@udf.setter
6363
def udf(self, value: bool):
6464
if value is True:
65-
msg = (
65+
msg = bfe.format_message(
6666
"BigFrames managed function (udf) is still under experiments. "
6767
"It may not work and subject to change in the future."
6868
)

bigframes/core/array_value.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ def from_table(
107107
if offsets_col and primary_key:
108108
raise ValueError("must set at most one of 'offests', 'primary_key'")
109109
if any(i.field_type == "JSON" for i in table.schema if i.name in schema.names):
110-
msg = (
111-
"Interpreting JSON column(s) as the `db_dtypes.dbjson` extension type is"
110+
msg = bfe.format_message(
111+
"Interpreting JSON column(s) as the `db_dtypes.dbjson` extension type is "
112112
"in preview; this behavior may change in future versions."
113113
)
114114
warnings.warn(msg, bfe.PreviewWarning)
@@ -232,7 +232,9 @@ def slice(
232232
self, start: Optional[int], stop: Optional[int], step: Optional[int]
233233
) -> ArrayValue:
234234
if self.node.order_ambiguous and not (self.session._strictly_ordered):
235-
msg = "Window ordering may be ambiguous, this can cause unstable results."
235+
msg = bfe.format_message(
236+
"Window ordering may be ambiguous, this can cause unstable results."
237+
)
236238
warnings.warn(msg, bfe.AmbiguousWindowWarning)
237239
return ArrayValue(
238240
nodes.SliceNode(
@@ -254,7 +256,7 @@ def promote_offsets(self) -> Tuple[ArrayValue, str]:
254256
"Generating offsets not supported in partial ordering mode"
255257
)
256258
else:
257-
msg = (
259+
msg = bfe.format_message(
258260
"Window ordering may be ambiguous, this can cause unstable results."
259261
)
260262
warnings.warn(msg, category=bfe.AmbiguousWindowWarning)
@@ -417,7 +419,9 @@ def project_window_op(
417419
"Generating offsets not supported in partial ordering mode"
418420
)
419421
else:
420-
msg = "Window ordering may be ambiguous, this can cause unstable results."
422+
msg = bfe.format_message(
423+
"Window ordering may be ambiguous, this can cause unstable results."
424+
)
421425
warnings.warn(msg, category=bfe.AmbiguousWindowWarning)
422426

423427
output_name = self._gen_namespaced_uid()

bigframes/core/blocks.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import bigframes.core.utils as utils
6565
import bigframes.core.window_spec as windows
6666
import bigframes.dtypes
67+
import bigframes.exceptions as bfe
6768
import bigframes.features
6869
import bigframes.operations as ops
6970
import bigframes.operations.aggregations as agg_ops
@@ -630,12 +631,12 @@ def _materialize_local(
630631
# Since we cannot acquire the table size without a query_job,
631632
# we skip the sampling.
632633
if sample_config.enable_downsampling:
633-
warnings.warn(
634+
msg = bfe.format_message(
634635
"Sampling is disabled and there is no download size limit when 'allow_large_results' is set to "
635636
"False. To prevent downloading excessive data, it is recommended to use the peek() method, or "
636-
"limit the data with methods like .head() or .sample() before proceeding with downloads.",
637-
UserWarning,
637+
"limit the data with methods like .head() or .sample() before proceeding with downloads."
638638
)
639+
warnings.warn(msg, category=UserWarning)
639640
fraction = 2
640641

641642
# TODO: Maybe materialize before downsampling
@@ -652,7 +653,7 @@ def _materialize_local(
652653
" # Setting it to None will download all the data\n"
653654
f"{constants.FEEDBACK_LINK}"
654655
)
655-
msg = (
656+
msg = bfe.format_message(
656657
f"The data size ({table_mb:.2f} MB) exceeds the maximum download limit of"
657658
f"({max_download_size} MB). It will be downsampled to {max_download_size} "
658659
"MB for download.\nPlease refer to the documentation for configuring "

bigframes/core/compile/aggregate_compiler.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def _(
165165
) -> ibis_types.NumericValue:
166166
# Will be null if all inputs are null. Pandas defaults to zero sum though.
167167
bq_sum = _apply_window_if_present(column.sum(), window)
168-
return bq_sum.fillna(ibis_types.literal(0))
168+
return bq_sum.fill_null(ibis_types.literal(0))
169169

170170

171171
@compile_unary_agg.register
@@ -610,12 +610,7 @@ def _(
610610
result = _apply_window_if_present(_is_true(column).all(), window)
611611
literal = ibis_types.literal(True)
612612

613-
return cast(
614-
ibis_types.BooleanScalar,
615-
result.fill_null(literal)
616-
if hasattr(result, "fill_null")
617-
else result.fillna(literal),
618-
)
613+
return cast(ibis_types.BooleanScalar, result.fill_null(literal))
619614

620615

621616
@compile_unary_agg.register
@@ -628,12 +623,7 @@ def _(
628623
result = _apply_window_if_present(_is_true(column).any(), window)
629624
literal = ibis_types.literal(False)
630625

631-
return cast(
632-
ibis_types.BooleanScalar,
633-
result.fill_null(literal)
634-
if hasattr(result, "fill_null")
635-
else result.fillna(literal),
636-
)
626+
return cast(ibis_types.BooleanScalar, result.fill_null(literal))
637627

638628

639629
@compile_ordered_unary_agg.register

bigframes/core/global_session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def _try_close_session(session: bigframes.session.Session):
3939
session_id = session.session_id
4040
location = session._location
4141
project_id = session._project
42-
msg = (
42+
msg = bfe.format_message(
4343
f"Session cleanup failed for session with id: {session_id}, "
4444
f"location: {location}, project: {project_id}"
4545
)

bigframes/core/indexers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ def _struct_accessor_check_and_warn(
407407
return
408408

409409
if not bigframes.dtypes.is_string_like(series.index.dtype):
410-
msg = (
410+
msg = bfe.format_message(
411411
"Are you trying to access struct fields? If so, please use Series.struct.field(...) "
412412
"method instead."
413413
)

bigframes/core/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def decorator(func):
196196

197197
@functools.wraps(func)
198198
def wrapper(*args, **kwargs):
199-
warnings.warn(msg, category=bfe.PreviewWarning)
199+
warnings.warn(bfe.format_message(msg), category=bfe.PreviewWarning)
200200
return func(*args, **kwargs)
201201

202202
return wrapper

bigframes/dataframe.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,7 +1581,10 @@ def to_arrow(
15811581
Returns:
15821582
pyarrow.Table: A pyarrow Table with all rows and columns of this DataFrame.
15831583
"""
1584-
msg = "to_arrow is in preview. Types and unnamed / duplicate name columns may change in future."
1584+
msg = bfe.format_message(
1585+
"to_arrow is in preview. Types and unnamed or duplicate name columns may "
1586+
"change in future."
1587+
)
15851588
warnings.warn(msg, category=bfe.PreviewWarning)
15861589

15871590
pa_table, query_job = self._block.to_arrow(
@@ -4104,7 +4107,7 @@ def apply(self, func, *, axis=0, args: typing.Tuple = (), **kwargs):
41044107
# to the applied function should be a Series, not a scalar.
41054108

41064109
if utils.get_axis_number(axis) == 1:
4107-
msg = "axis=1 scenario is in preview."
4110+
msg = bfe.format_message("axis=1 scenario is in preview.")
41084111
warnings.warn(msg, category=bfe.PreviewWarning)
41094112

41104113
# TODO(jialuo): Deprecate the "bigframes_remote_function" attribute.

bigframes/exceptions.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
"""Public exceptions and warnings used across BigQuery DataFrames."""
1616

17+
import textwrap
18+
1719
# NOTE: This module should not depend on any others in the package.
1820

1921

@@ -87,3 +89,27 @@ class ApiDeprecationWarning(FutureWarning):
8789

8890
class BadIndexerKeyWarning(Warning):
8991
"""The indexer key is not used correctly."""
92+
93+
94+
class ColorFormatter:
95+
WARNING = "\033[93m"
96+
ENDC = "\033[0m"
97+
98+
99+
def format_message(message: str, fill: bool = True):
100+
"""Formats a warning message with ANSI color codes for the warning color.
101+
102+
Args:
103+
message: The warning message string.
104+
fill: Whether to wrap the message text using `textwrap.fill`.
105+
Defaults to True. Set to False to prevent wrapping,
106+
especially if the message already contains newlines.
107+
108+
Returns:
109+
The formatted message string, with ANSI color codes for warning color
110+
if color is supported, otherwise the original message. If `fill` is
111+
True, the message will be wrapped to fit the terminal width.
112+
"""
113+
if fill:
114+
message = textwrap.fill(message)
115+
return ColorFormatter.WARNING + message + ColorFormatter.ENDC

0 commit comments

Comments
 (0)