Skip to content

Commit 83cf0cc

Browse files
committed
add error mode
1 parent bfe0837 commit 83cf0cc

File tree

2 files changed

+50
-10
lines changed

2 files changed

+50
-10
lines changed

plotly/basedatatypes.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ def _emit_recommendations(obj, context):
3535
context is one of "figure", "trace", "layout".
3636
Lazy import to avoid circular imports.
3737
"""
38+
from plotly.recommendations import run_recommendations, RecommendationError
3839
try:
39-
from plotly.recommendations import run_recommendations
40-
4140
run_recommendations(obj, context)
41+
except RecommendationError:
42+
raise
4243
except Exception:
4344
pass
4445

@@ -5224,6 +5225,12 @@ def update(self, dict1=None, overwrite=False, **kwargs):
52245225
BaseFigure._perform_update(self, dict1, overwrite=overwrite)
52255226
BaseFigure._perform_update(self, kwargs, overwrite=overwrite)
52265227

5228+
# Recommendations mode: run after update so Express (trace.update(patch)) is covered
5229+
if isinstance(self, BaseTraceType):
5230+
_emit_recommendations(self, "trace")
5231+
elif isinstance(self, BaseLayoutType):
5232+
_emit_recommendations(self, "layout")
5233+
52275234
return self
52285235

52295236
def pop(self, key, *args):

plotly/recommendations.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- Environment: set PLOTLY_RECOMMENDATIONS=1 (or "true", "yes") to enable.
88
- In code: plotly.recommendations.config.enabled = True
99
10+
Mode: plotly.recommendations.config.mode can be "warn" (default) or "error".
11+
- "warn": emit a UserWarning when a recommendation is violated.
12+
- "error": raise RecommendationError when a recommendation is violated.
13+
1014
Example:
1115
import plotly.recommendations
1216
import plotly.graph_objects as go
@@ -21,24 +25,39 @@
2125
import os
2226
import warnings
2327

28+
_VALID_MODES = ("warn", "error")
29+
30+
2431
# -----------------------------------------------------------------------------
25-
# Global enable/disable
32+
# Exception and config
2633
# -----------------------------------------------------------------------------
2734

35+
36+
class RecommendationError(ValueError):
37+
"""Raised when a recommendation is violated and config.mode is 'error'."""
38+
39+
2840
def _env_enabled():
2941
v = os.environ.get("PLOTLY_RECOMMENDATIONS", "").strip().lower()
3042
return v in ("1", "true", "yes", "on")
3143

3244

45+
def _env_mode():
46+
v = os.environ.get("PLOTLY_RECOMMENDATIONS_MODE", "").strip().lower()
47+
return v if v in _VALID_MODES else "warn"
48+
49+
50+
3351
class _RecommendationsConfig:
3452
"""
3553
Global config for recommendations mode.
3654
When enabled, recommendation checkers run after Figure/trace/Layout
37-
construction and may emit warnings.
55+
construction. Use config.mode to choose "warn" (emit warnings) or "error" (raise).
3856
"""
3957

4058
def __init__(self):
4159
self._enabled = _env_enabled()
60+
self._mode = _env_mode()
4261

4362
@property
4463
def enabled(self):
@@ -48,6 +67,19 @@ def enabled(self):
4867
def enabled(self, value):
4968
self._enabled = bool(value)
5069

70+
@property
71+
def mode(self):
72+
return self._mode
73+
74+
@mode.setter
75+
def mode(self, value):
76+
if value not in _VALID_MODES:
77+
raise ValueError(
78+
"config.mode must be one of %s, got %r" % (_VALID_MODES, value)
79+
)
80+
self._mode = value
81+
82+
5183
# Singleton used by the rest of the package
5284
config = _RecommendationsConfig()
5385

@@ -133,7 +165,7 @@ def max_length(input_list, max_length):
133165
Check if input_list has length >= max_length (e.g. recommended to keep shorter).
134166
Returns a string describing the issue, or None if no issue was found.
135167
"""
136-
if input_list is not None and hasattr(input_list, "__len__") and len(input_list) >= max_length:
168+
if input_list is not None and hasattr(input_list, "__len__") and len(input_list) > max_length:
137169
return f"has length {len(input_list)} (recommended <= {max_length})."
138170
return None
139171

@@ -185,12 +217,13 @@ def run_recommendations(obj, context):
185217
for value_tuple in zip(*value_lists):
186218
issue = checker(*value_tuple)
187219
if issue:
220+
msg = f"{path_def}: {issue}"
221+
if config.mode == "error":
222+
raise RecommendationError(msg)
188223
stacklevel = stacklevel or _get_stacklevel()
189-
warnings.warn(
190-
f"{path_def}: {issue}",
191-
UserWarning,
192-
stacklevel=stacklevel,
193-
)
224+
warnings.warn(msg, UserWarning, stacklevel=stacklevel)
225+
except RecommendationError:
226+
raise
194227
except Exception:
195228
pass
196229

0 commit comments

Comments
 (0)