Skip to content

Commit 0c5665f

Browse files
authored
Secondary y-axis subplot support (#1564)
Introduces secondary y-axis support to the subplots.make_subplots and associated functions. This functionality is only available in the v4_subplots future mode.
1 parent a4b886c commit 0c5665f

File tree

10 files changed

+1519
-312
lines changed

10 files changed

+1519
-312
lines changed

Diff for: codegen/datatypes.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -491,11 +491,16 @@ def add_docstring(buffer, node, header, prepend_extras=(), append_extras=()):
491491

492492
# Write any append extras
493493
for p, v in append_extras:
494-
v_wrapped = '\n'.join(textwrap.wrap(
495-
v,
496-
width=79-12,
497-
initial_indent=' ' * 12,
498-
subsequent_indent=' ' * 12))
494+
if '\n' in v:
495+
# If v contains newlines then assume it's already wrapped as
496+
# desired
497+
v_wrapped = v
498+
else:
499+
v_wrapped = '\n'.join(textwrap.wrap(
500+
v,
501+
width=79-12,
502+
initial_indent=' ' * 12,
503+
subsequent_indent=' ' * 12))
499504
buffer.write(f"""
500505
{p}
501506
{v_wrapped}""")

Diff for: codegen/figure.py

+102-23
Original file line numberDiff line numberDiff line change
@@ -107,28 +107,72 @@ def __init__(self, data=None, layout=None,
107107
# ### add_trace methods for each trace type ###
108108
for trace_node in trace_nodes:
109109

110+
include_secondary_y = bool([
111+
d for d in trace_node.child_datatypes
112+
if d.name_property == 'yaxis'
113+
])
114+
if include_secondary_y:
115+
secondary_y_1 = ', secondary_y=None'
116+
secondary_y_2 = ', secondary_y=secondary_y'
117+
secondary_y_docstring = f"""
118+
secondary_y: boolean or None (default None)
119+
* If True, only select yaxis objects associated with the secondary
120+
y-axis of the subplot.
121+
* If False, only select yaxis objects associated with the primary
122+
y-axis of the subplot.
123+
* If None (the default), do not filter yaxis objects based on
124+
a secondary y-axis condition.
125+
126+
To select yaxis objects by secondary y-axis, the Figure must
127+
have been created using plotly.subplots.make_subplots. See
128+
the docstring for the specs argument to make_subplots for more
129+
info on creating subplots with secondary y-axes."""
130+
else:
131+
secondary_y_1 = ''
132+
secondary_y_2 = ''
133+
secondary_y_docstring = ''
134+
110135
# #### Function signature ####
111136
buffer.write(f"""
112137
def add_{trace_node.plotly_name}(self""")
113138

114139
# #### Function params####
140+
param_extras = ['row', 'col']
141+
if include_secondary_y:
142+
param_extras.append('secondary_y')
115143
add_constructor_params(buffer,
116144
trace_node.child_datatypes,
117-
append_extras=['row', 'col'])
145+
append_extras=param_extras)
118146

119147
# #### Docstring ####
120148
header = f"Add a new {trace_node.name_datatype_class} trace"
121149

122-
extras = (('row : int or None (default)',
123-
'Subplot row index (starting from 1) for the trace to be '
124-
'added. Only valid if figure was created using '
125-
'`plotly.tools.make_subplots`'),
126-
('col : int or None (default)',
127-
'Subplot col index (starting from 1) for the trace to be '
128-
'added. Only valid if figure was created using '
129-
'`plotly.tools.make_subplots`'))
130-
131-
add_docstring(buffer, trace_node, header, append_extras=extras)
150+
doc_extras = [(
151+
'row : int or None (default)',
152+
'Subplot row index (starting from 1) for the trace to be '
153+
'added. Only valid if figure was created using '
154+
'`plotly.tools.make_subplots`'),
155+
('col : int or None (default)',
156+
'Subplot col index (starting from 1) for the trace to be '
157+
'added. Only valid if figure was created using '
158+
'`plotly.tools.make_subplots`')]
159+
160+
if include_secondary_y:
161+
doc_extras.append(
162+
('secondary_y: boolean or None (default None)', """\
163+
If True, associate this trace with the secondary y-axis of the
164+
subplot at the specified row and col. Only valid if all of the
165+
following conditions are satisfied:
166+
* The figure was created using `plotly.subplots.make_subplots`.
167+
* The row and col arguments are not None
168+
* The subplot at the specified row and col has type xy
169+
(which is the default) and secondary_y True. These
170+
properties are specified in the specs argument to
171+
make_subplots. See the make_subplots docstring for more info.\
172+
""")
173+
)
174+
175+
add_docstring(buffer, trace_node, header, append_extras=doc_extras)
132176

133177
# #### Function body ####
134178
buffer.write(f"""
@@ -143,18 +187,47 @@ def add_{trace_node.plotly_name}(self""")
143187
buffer.write(f"""
144188
**kwargs)""")
145189

190+
if include_secondary_y:
191+
secondary_y_kwarg = ', secondary_y=secondary_y'
192+
else:
193+
secondary_y_kwarg = ''
194+
146195
buffer.write(f"""
147-
return self.add_trace(new_trace, row=row, col=col)""")
196+
return self.add_trace(
197+
new_trace, row=row, col=col{secondary_y_kwarg})""")
148198

149199
# update layout subplots
150200
# ----------------------
151201
inflect_eng = inflect.engine()
152202
for subplot_node in subplot_nodes:
153203
singular_name = subplot_node.name_property
154204
plural_name = inflect_eng.plural_noun(singular_name)
205+
206+
if singular_name == 'yaxis':
207+
secondary_y_1 = ', secondary_y=None'
208+
secondary_y_2 = ', secondary_y=secondary_y'
209+
secondary_y_docstring = f"""
210+
secondary_y: boolean or None (default None)
211+
* If True, only select yaxis objects associated with the secondary
212+
y-axis of the subplot.
213+
* If False, only select yaxis objects associated with the primary
214+
y-axis of the subplot.
215+
* If None (the default), do not filter yaxis objects based on
216+
a secondary y-axis condition.
217+
218+
To select yaxis objects by secondary y-axis, the Figure must
219+
have been created using plotly.subplots.make_subplots. See
220+
the docstring for the specs argument to make_subplots for more
221+
info on creating subplots with secondary y-axes."""
222+
else:
223+
secondary_y_1 = ''
224+
secondary_y_2 = ''
225+
secondary_y_docstring = ''
226+
155227
buffer.write(f"""
156228
157-
def select_{plural_name}(self, selector=None, row=None, col=None):
229+
def select_{plural_name}(
230+
self, selector=None, row=None, col=None{secondary_y_1}):
158231
\"\"\"
159232
Select {singular_name} subplot objects from a particular subplot cell
160233
and/or {singular_name} subplot objects that satisfy custom selection
@@ -172,8 +245,8 @@ def select_{plural_name}(self, selector=None, row=None, col=None):
172245
Subplot row and column index of {singular_name} objects to select.
173246
To select {singular_name} objects by row and column, the Figure
174247
must have been created using plotly.subplots.make_subplots.
175-
If None (the default), all {singular_name} objects are selected.
176-
248+
If None (the default), all {singular_name} objects are selected.\
249+
{secondary_y_docstring}
177250
Returns
178251
-------
179252
generator
@@ -184,9 +257,10 @@ def select_{plural_name}(self, selector=None, row=None, col=None):
184257
_validate_v4_subplots('select_{plural_name}')
185258
186259
return self._select_layout_subplots_by_prefix(
187-
'{singular_name}', selector, row, col)
260+
'{singular_name}', selector, row, col{secondary_y_2})
188261
189-
def for_each_{singular_name}(self, fn, selector=None, row=None, col=None):
262+
def for_each_{singular_name}(
263+
self, fn, selector=None, row=None, col=None{secondary_y_1}):
190264
\"\"\"
191265
Apply a function to all {singular_name} objects that satisfy the
192266
specified selection criteria
@@ -205,21 +279,25 @@ def for_each_{singular_name}(self, fn, selector=None, row=None, col=None):
205279
Subplot row and column index of {singular_name} objects to select.
206280
To select {singular_name} objects by row and column, the Figure
207281
must have been created using plotly.subplots.make_subplots.
208-
If None (the default), all {singular_name} objects are selected.
209-
282+
If None (the default), all {singular_name} objects are selected.\
283+
{secondary_y_docstring}
210284
Returns
211285
-------
212286
self
213287
Returns the Figure object that the method was called on
214288
\"\"\"
215289
for obj in self.select_{plural_name}(
216-
selector=selector, row=row, col=col):
290+
selector=selector, row=row, col=col{secondary_y_2}):
217291
fn(obj)
218292
219293
return self
220294
221295
def update_{plural_name}(
222-
self, patch=None, selector=None, row=None, col=None, **kwargs):
296+
self,
297+
patch=None,
298+
selector=None,
299+
row=None, col=None{secondary_y_1},
300+
**kwargs):
223301
\"\"\"
224302
Perform a property update operation on all {singular_name} objects
225303
that satisfy the specified selection criteria
@@ -239,7 +317,8 @@ def update_{plural_name}(
239317
Subplot row and column index of {singular_name} objects to select.
240318
To select {singular_name} objects by row and column, the Figure
241319
must have been created using plotly.subplots.make_subplots.
242-
If None (the default), all {singular_name} objects are selected.
320+
If None (the default), all {singular_name} objects are selected.\
321+
{secondary_y_docstring}
243322
**kwargs
244323
Additional property updates to apply to each selected
245324
{singular_name} object. If a property is specified in
@@ -251,7 +330,7 @@ def update_{plural_name}(
251330
Returns the Figure object that the method was called on
252331
\"\"\"
253332
for obj in self.select_{plural_name}(
254-
selector=selector, row=row, col=col):
333+
selector=selector, row=row, col=col{secondary_y_2}):
255334
obj.update(patch, **kwargs)
256335
257336
return self""")

0 commit comments

Comments
 (0)