Skip to content

Commit 106426e

Browse files
committed
Add initial template validation logic
1 parent 2e5bf6e commit 106426e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+2529
-138
lines changed

Diff for: codegen/__init__.py

+31
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
write_data_validator_py,
1414
get_data_validator_instance)
1515

16+
1617
# Import notes
1718
# ------------
1819
# Nothing from the plotly/ package should be imported during code
@@ -22,6 +23,32 @@
2223
# codegen/ package, and helpers used both during code generation and at
2324
# runtime should reside in the _plotly_utils/ package.
2425
# ----------------------------------------------------------------------------
26+
def preprocess_schema(plotly_schema):
27+
"""
28+
Central location to make changes to schema before it's seen by the
29+
PlotlyNode classes
30+
"""
31+
layout = plotly_schema['layout']['layoutAttributes']
32+
33+
template_description = layout['template']['description']
34+
35+
# Create codegen-friendly template scheme
36+
template = {
37+
"data": {
38+
trace + 's': {
39+
'items': {
40+
trace: {
41+
},
42+
},
43+
"role": "object"
44+
}
45+
for trace in plotly_schema['traces']
46+
},
47+
"layout": {
48+
}
49+
}
50+
51+
layout['template'] = template
2552

2653

2754
def perform_codegen():
@@ -52,6 +79,10 @@ def perform_codegen():
5279
with open('plotly/package_data/plot-schema.json', 'r') as f:
5380
plotly_schema = json.load(f)
5481

82+
# Preprocess Schema
83+
# -----------------
84+
preprocess_schema(plotly_schema)
85+
5586
# Build node lists
5687
# ----------------
5788
# ### TraceNode ###

Diff for: codegen/datatypes.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ def build_datatype_py(node):
6666
# ---------------
6767
assert node.is_compound
6868

69+
# Handle template traces
70+
# ----------------------
71+
# We want template trace/layout classes like
72+
# plotly.graph_objs.layout.template.data.Scatter to map to the
73+
# corresponding trace/layout class (e.g. plotly.graph_objs.Scatter).
74+
# So rather than generate a class definition, we just import the
75+
# corresponding trace/layout class
76+
if node.parent_path_str == 'layout.template.data':
77+
return f"from plotly.graph_objs import {node.name_datatype_class}"
78+
elif node.path_str == 'layout.template.layout':
79+
return "from plotly.graph_objs import Layout"
80+
6981
# Extract node properties
7082
# -----------------------
7183
undercase = node.name_undercase
@@ -244,7 +256,17 @@ def __init__(self""")
244256
# ----------------------------------""")
245257
for subtype_node in subtype_nodes:
246258
name_prop = subtype_node.name_property
247-
buffer.write(f"""
259+
if name_prop == 'template':
260+
# Special handling for layout.template to avoid infinite
261+
# recursion. Only initialize layout.template object if non-None
262+
# value specified
263+
buffer.write(f"""
264+
_v = arg.pop('{name_prop}', None)
265+
_v = {name_prop} if {name_prop} is not None else _v
266+
if _v is not None:
267+
self['{name_prop}'] = _v""")
268+
else:
269+
buffer.write(f"""
248270
_v = arg.pop('{name_prop}', None)
249271
self['{name_prop}'] = {name_prop} \
250272
if {name_prop} is not None else _v""")

Diff for: codegen/utils.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,14 @@ def __init__(self, plotly_schema, node_path=(), parent=None):
257257
# Note the node_data is a property that must be computed by the
258258
# subclass based on plotly_schema and node_path
259259
if isinstance(self.node_data, dict_like):
260+
childs_parent = (
261+
parent
262+
if self.node_path and self.node_path[-1] == 'items'
263+
else self)
264+
260265
self._children = [self.__class__(self.plotly_schema,
261266
node_path=self.node_path + (c,),
262-
parent=self)
267+
parent=childs_parent)
263268
for c in self.node_data if c and c[0] != '_']
264269

265270
# Sort by plotly name
@@ -387,7 +392,15 @@ def name_property(self):
387392
-------
388393
str
389394
"""
390-
return self.plotly_name + ('s' if self.is_array_element else '')
395+
396+
return self.plotly_name + (
397+
's' if self.is_array_element and
398+
# Don't add 's' to layout.template.data.scatter etc.
399+
not (self.parent and
400+
self.parent.parent and
401+
self.parent.parent.parent and
402+
self.parent.parent.parent.name_property == 'template')
403+
else '')
391404

392405
@property
393406
def name_validator_class(self) -> str:
@@ -600,8 +613,8 @@ def is_array_element(self):
600613
-------
601614
bool
602615
"""
603-
if self.parent and self.parent.parent:
604-
return self.parent.parent.is_array
616+
if self.parent:
617+
return self.parent.is_array
605618
else:
606619
return False
607620

Diff for: plotly/graph_objs/_figure.py

+2-23
Original file line numberDiff line numberDiff line change
@@ -277,29 +277,8 @@ def __init__(
277277
hovered on but will not generate spikelines,
278278
such as scatter fills.
279279
template
280-
Default attributes to be applied to the plot.
281-
Templates can be created from existing plots
282-
using `Plotly.makeTemplate`, or created
283-
manually. They should be objects with format:
284-
`{layout: layoutTemplate, data: {[type]:
285-
[traceTemplate, ...]}, ...}` `layoutTemplate`
286-
and `traceTemplate` are objects matching the
287-
attribute structure of `layout` and a data
288-
trace. Trace templates are applied cyclically
289-
to traces of each type. Container arrays (eg
290-
`annotations`) have special handling: An object
291-
ending in `defaults` (eg `annotationdefaults`)
292-
is applied to each array item. But if an item
293-
has a `templateitemname` key we look in the
294-
template array for an item with matching `name`
295-
and apply that instead. If no matching `name`
296-
is found we mark the item invisible. Any named
297-
template item not referenced is appended to the
298-
end of the array, so you can use this for a
299-
watermark annotation or a logo image, for
300-
example. To omit one of these items on the
301-
plot, make an item with matching
302-
`templateitemname` and `visible: false`.
280+
plotly.graph_objs.layout.Template instance or
281+
dict with compatible properties
303282
ternary
304283
plotly.graph_objs.layout.Ternary instance or
305284
dict with compatible properties

Diff for: plotly/graph_objs/_figurewidget.py

+2-23
Original file line numberDiff line numberDiff line change
@@ -277,29 +277,8 @@ def __init__(
277277
hovered on but will not generate spikelines,
278278
such as scatter fills.
279279
template
280-
Default attributes to be applied to the plot.
281-
Templates can be created from existing plots
282-
using `Plotly.makeTemplate`, or created
283-
manually. They should be objects with format:
284-
`{layout: layoutTemplate, data: {[type]:
285-
[traceTemplate, ...]}, ...}` `layoutTemplate`
286-
and `traceTemplate` are objects matching the
287-
attribute structure of `layout` and a data
288-
trace. Trace templates are applied cyclically
289-
to traces of each type. Container arrays (eg
290-
`annotations`) have special handling: An object
291-
ending in `defaults` (eg `annotationdefaults`)
292-
is applied to each array item. But if an item
293-
has a `templateitemname` key we look in the
294-
template array for an item with matching `name`
295-
and apply that instead. If no matching `name`
296-
is found we mark the item invisible. Any named
297-
template item not referenced is appended to the
298-
end of the array, so you can use this for a
299-
watermark annotation or a logo image, for
300-
example. To omit one of these items on the
301-
plot, make an item with matching
302-
`templateitemname` and `visible: false`.
280+
plotly.graph_objs.layout.Template instance or
281+
dict with compatible properties
303282
ternary
304283
plotly.graph_objs.layout.Ternary instance or
305284
dict with compatible properties

Diff for: plotly/graph_objs/_layout.py

+21-61
Original file line numberDiff line numberDiff line change
@@ -2140,30 +2140,24 @@ def spikedistance(self, val):
21402140
@property
21412141
def template(self):
21422142
"""
2143-
Default attributes to be applied to the plot. Templates can be
2144-
created from existing plots using `Plotly.makeTemplate`, or
2145-
created manually. They should be objects with format: `{layout:
2146-
layoutTemplate, data: {[type]: [traceTemplate, ...]}, ...}`
2147-
`layoutTemplate` and `traceTemplate` are objects matching the
2148-
attribute structure of `layout` and a data trace. Trace
2149-
templates are applied cyclically to traces of each type.
2150-
Container arrays (eg `annotations`) have special handling: An
2151-
object ending in `defaults` (eg `annotationdefaults`) is
2152-
applied to each array item. But if an item has a
2153-
`templateitemname` key we look in the template array for an
2154-
item with matching `name` and apply that instead. If no
2155-
matching `name` is found we mark the item invisible. Any named
2156-
template item not referenced is appended to the end of the
2157-
array, so you can use this for a watermark annotation or a logo
2158-
image, for example. To omit one of these items on the plot,
2159-
make an item with matching `templateitemname` and `visible:
2160-
false`.
2143+
The 'template' property is an instance of Template
2144+
that may be specified as:
2145+
- An instance of plotly.graph_objs.layout.Template
2146+
- A dict of string/value properties that will be passed
2147+
to the Template constructor
21612148
2162-
The 'template' property accepts values of any type
2149+
Supported dict properties:
2150+
2151+
data
2152+
plotly.graph_objs.layout.template.Data instance
2153+
or dict with compatible properties
2154+
layout
2155+
plotly.graph_objs.layout.template.Layout
2156+
instance or dict with compatible properties
21632157
21642158
Returns
21652159
-------
2166-
Any
2160+
plotly.graph_objs.layout.Template
21672161
"""
21682162
return self['template']
21692163

@@ -3446,26 +3440,8 @@ def _prop_descriptions(self):
34463440
objects can be hovered on but will not generate
34473441
spikelines, such as scatter fills.
34483442
template
3449-
Default attributes to be applied to the plot. Templates
3450-
can be created from existing plots using
3451-
`Plotly.makeTemplate`, or created manually. They should
3452-
be objects with format: `{layout: layoutTemplate, data:
3453-
{[type]: [traceTemplate, ...]}, ...}` `layoutTemplate`
3454-
and `traceTemplate` are objects matching the attribute
3455-
structure of `layout` and a data trace. Trace
3456-
templates are applied cyclically to traces of each
3457-
type. Container arrays (eg `annotations`) have special
3458-
handling: An object ending in `defaults` (eg
3459-
`annotationdefaults`) is applied to each array item.
3460-
But if an item has a `templateitemname` key we look in
3461-
the template array for an item with matching `name` and
3462-
apply that instead. If no matching `name` is found we
3463-
mark the item invisible. Any named template item not
3464-
referenced is appended to the end of the array, so you
3465-
can use this for a watermark annotation or a logo
3466-
image, for example. To omit one of these items on the
3467-
plot, make an item with matching `templateitemname` and
3468-
`visible: false`.
3443+
plotly.graph_objs.layout.Template instance or dict with
3444+
compatible properties
34693445
ternary
34703446
plotly.graph_objs.layout.Ternary instance or dict with
34713447
compatible properties
@@ -3770,26 +3746,8 @@ def __init__(
37703746
objects can be hovered on but will not generate
37713747
spikelines, such as scatter fills.
37723748
template
3773-
Default attributes to be applied to the plot. Templates
3774-
can be created from existing plots using
3775-
`Plotly.makeTemplate`, or created manually. They should
3776-
be objects with format: `{layout: layoutTemplate, data:
3777-
{[type]: [traceTemplate, ...]}, ...}` `layoutTemplate`
3778-
and `traceTemplate` are objects matching the attribute
3779-
structure of `layout` and a data trace. Trace
3780-
templates are applied cyclically to traces of each
3781-
type. Container arrays (eg `annotations`) have special
3782-
handling: An object ending in `defaults` (eg
3783-
`annotationdefaults`) is applied to each array item.
3784-
But if an item has a `templateitemname` key we look in
3785-
the template array for an item with matching `name` and
3786-
apply that instead. If no matching `name` is found we
3787-
mark the item invisible. Any named template item not
3788-
referenced is appended to the end of the array, so you
3789-
can use this for a watermark annotation or a logo
3790-
image, for example. To omit one of these items on the
3791-
plot, make an item with matching `templateitemname` and
3792-
`visible: false`.
3749+
plotly.graph_objs.layout.Template instance or dict with
3750+
compatible properties
37933751
ternary
37943752
plotly.graph_objs.layout.Ternary instance or dict with
37953753
compatible properties
@@ -4010,7 +3968,9 @@ def __init__(
40103968
self['spikedistance'
40113969
] = spikedistance if spikedistance is not None else _v
40123970
_v = arg.pop('template', None)
4013-
self['template'] = template if template is not None else _v
3971+
_v = template if template is not None else _v
3972+
if _v is not None:
3973+
self['template'] = _v
40143974
_v = arg.pop('ternary', None)
40153975
self['ternary'] = ternary if ternary is not None else _v
40163976
_v = arg.pop('title', None)

Diff for: plotly/graph_objs/layout/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from ._titlefont import Titlefont
88
from ._ternary import Ternary
99
from plotly.graph_objs.layout import ternary
10+
from ._template import Template
11+
from plotly.graph_objs.layout import template
1012
from ._slider import Slider
1113
from plotly.graph_objs.layout import slider
1214
from ._shape import Shape

0 commit comments

Comments
 (0)