Skip to content

Commit 25c9845

Browse files
yqtian-seclaude
andcommitted
Fix #5613: Apply impliedEdits for contours initialization
When creating Contour, Histogram2dContour, or Contourcarpet traces with contours.size, contours.start, or contours.end properties, the autocontour property should be automatically set to False to respect the manual contour specifications. The Plotly schema defines impliedEdits that set autocontour=False when these properties are specified. Previously, this only worked for dynamic updates via restyle (e.g., dropdown menus), but not for initial trace creation. Changes: - Modified codegen/datatypes.py to add get_implied_edits_code() function that generates Python code to apply impliedEdits during initialization - Updated build_datatype_py() to call get_implied_edits_code() and insert the generated code into __init__ methods The auto-generated graph_objs files should be regenerated by running: python codegen/__init__.py Fixes: #5613 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 50bb20b commit 25c9845

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55
## Unreleased
66

77
### Fixed
8+
- Fix issue where `contours.size`, `contours.start`, and `contours.end` properties were ignored during initial render of Contour, Histogram2dContour, and Contourcarpet traces by applying impliedEdits logic from the schema during initialization [[#5613](https://github.com/plotly/plotly.py/issues/5613)]
89
- Raise a clear `ValueError` when an unsupported marginal plot type is passed to Plotly Express, instead of failing later with a cryptic `'NoneType' object has no attribute 'constructor'` message [[#5625](https://github.com/plotly/plotly.py/pull/5625)], with thanks to @eugen-goebel for the contribution!
910

1011

codegen/datatypes.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,80 @@ def get_typing_type(plotly_type, array_ok=False):
5656
return pytype
5757

5858

59+
def get_implied_edits_code(node):
60+
"""
61+
Generate Python code to apply impliedEdits during initialization.
62+
63+
This handles cases where setting certain properties should automatically
64+
set other properties. For example, when contours.size is set, autocontour
65+
should be set to False.
66+
67+
Parameters
68+
----------
69+
node : PlotlyNode
70+
The trace/datatype node to check for impliedEdits
71+
72+
Returns
73+
-------
74+
str or None
75+
Generated Python code, or None if no impliedEdits apply
76+
"""
77+
# Only apply to traces that have both autocontour and contours
78+
if node.name_property not in ["contour", "histogram2dcontour", "contourcarpet"]:
79+
return None
80+
81+
node_data = node.node_data
82+
if not isinstance(node_data, dict):
83+
return None
84+
85+
has_autocontour = "autocontour" in node_data
86+
has_contours = "contours" in node_data
87+
88+
if not (has_autocontour and has_contours):
89+
return None
90+
91+
# Check if contours property has children with impliedEdits
92+
contours_data = node_data.get("contours", {})
93+
if not isinstance(contours_data, dict):
94+
return None
95+
96+
# Look for properties that trigger autocontour=False
97+
trigger_props = []
98+
for prop_name in ["size", "start", "end"]:
99+
prop_data = contours_data.get(prop_name, {})
100+
if isinstance(prop_data, dict) and "impliedEdits" in prop_data:
101+
implied_edits = prop_data.get("impliedEdits", {})
102+
# Check if this property's impliedEdits sets autocontour to false
103+
if implied_edits.get("^autocontour") == False:
104+
trigger_props.append(prop_name)
105+
106+
if not trigger_props:
107+
return None
108+
109+
# Generate the code
110+
code = f'''
111+
# Apply impliedEdits: if any of contours.{"/".join(trigger_props)} are set,
112+
# autocontour should be False unless explicitly set by the user
113+
if "contours" in self._props and self._props["contours"] is not None:
114+
contours_obj = self._props["contours"]
115+
# contours_obj might be a dict or a Contours object
116+
if isinstance(contours_obj, dict):
117+
contours_dict = contours_obj
118+
elif hasattr(contours_obj, "_props"):
119+
contours_dict = contours_obj._props
120+
else:
121+
contours_dict = {{}}
122+
# Check if any of the properties that trigger autocontour=False are set
123+
triggers_autocontour_false = any(
124+
prop in contours_dict and contours_dict[prop] is not None
125+
for prop in {repr(trigger_props)}
126+
)
127+
if triggers_autocontour_false and autocontour is None:
128+
self._set_property("autocontour", {{}}, False)
129+
'''
130+
return code
131+
132+
59133
def build_datatype_py(node):
60134
"""
61135
Build datatype (graph_objs) class source code string for a datatype
@@ -404,6 +478,11 @@ def __init__(self"""
404478
arg.pop("{lit_name}", None)"""
405479
)
406480

481+
# Apply impliedEdits code if applicable
482+
implied_edits_code = get_implied_edits_code(node)
483+
if implied_edits_code:
484+
buffer.write(implied_edits_code)
485+
407486
buffer.write(
408487
"""
409488
self._process_kwargs(**dict(arg, **kwargs))

0 commit comments

Comments
 (0)