Skip to content

Commit 9dc08a1

Browse files
authored
Merge pull request #5214 from plotly/refactor-validators
refactor validators to remove duplicate files
2 parents 945c000 + 37c387e commit 9dc08a1

File tree

13,342 files changed

+134849
-258852
lines changed

Some content is hidden

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

13,342 files changed

+134849
-258852
lines changed

codegen/__init__.py

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
build_from_imports_py,
2222
)
2323
from codegen.validators import (
24-
write_validator_py,
25-
write_data_validator_py,
24+
get_data_validator_params,
25+
get_validator_params,
26+
write_validator_json,
2627
get_data_validator_instance,
2728
)
2829

@@ -171,22 +172,27 @@ def perform_codegen(reformat=True):
171172
if node.is_compound and not isinstance(node, ElementDefaultsNode)
172173
]
173174

175+
validator_params = {}
174176
# Write out validators
175177
# --------------------
176178
# # ### Layout ###
177179
for node in all_layout_nodes:
178-
write_validator_py(outdir, node)
180+
get_validator_params(node, validator_params)
179181

180182
# ### Trace ###
181183
for node in all_trace_nodes:
182-
write_validator_py(outdir, node)
184+
get_validator_params(node, validator_params)
183185

184186
# ### Frames ###
185187
for node in all_frame_nodes:
186-
write_validator_py(outdir, node)
188+
get_validator_params(node, validator_params)
187189

188190
# ### Data (traces) validator ###
189-
write_data_validator_py(outdir, base_traces_node)
191+
get_data_validator_params(base_traces_node, validator_params)
192+
193+
# Write out the JSON data for the validators
194+
os.makedirs(validators_pkgdir, exist_ok=True)
195+
write_validator_json(outdir, validator_params)
190196

191197
# Alls
192198
# ----
@@ -217,27 +223,6 @@ def perform_codegen(reformat=True):
217223
layout_array_nodes,
218224
)
219225

220-
# Write validator __init__.py files
221-
# ---------------------------------
222-
# ### Write __init__.py files for each validator package ###
223-
validator_rel_class_imports = {}
224-
for node in all_datatype_nodes:
225-
if node.is_mapped:
226-
continue
227-
key = node.parent_path_parts
228-
validator_rel_class_imports.setdefault(key, []).append(
229-
f"._{node.name_property}.{node.name_validator_class}"
230-
)
231-
232-
# Add Data validator
233-
root_validator_pairs = validator_rel_class_imports[()]
234-
root_validator_pairs.append("._data.DataValidator")
235-
236-
# Output validator __init__.py files
237-
validators_pkg = opath.join(outdir, "validators")
238-
for path_parts, rel_classes in validator_rel_class_imports.items():
239-
write_init_py(validators_pkg, path_parts, [], rel_classes)
240-
241226
# Write datatype __init__.py files
242227
# --------------------------------
243228
datatype_rel_class_imports = {}

codegen/datatypes.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,11 @@ class {datatype_class}(_{node.name_base_datatype}):\n"""
130130
"""
131131
)
132132

133-
subplot_validator_names = [n.name_validator_class for n in subplot_nodes]
134-
135-
validator_csv = ", ".join(subplot_validator_names)
136133
subplot_dict_str = (
137134
"{"
138135
+ ", ".join(
139-
f"'{subname}': {valname}"
140-
for subname, valname in zip(subplot_names, subplot_validator_names)
136+
f'"{subname}": ValidatorCache.get_validator("layout", "{subname}")'
137+
for subname in subplot_names
141138
)
142139
+ "}"
143140
)
@@ -153,7 +150,7 @@ def _subplotid_validators(self):
153150
-------
154151
dict
155152
\"\"\"
156-
from plotly.validators.layout import ({validator_csv})
153+
from plotly.validator_cache import ValidatorCache
157154
158155
return {subplot_dict_str}
159156

codegen/validators.py

Lines changed: 54 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,90 @@
11
import os.path as opath
22
from io import StringIO
3+
import json
34

45
import _plotly_utils.basevalidators
56
from codegen.utils import CAVEAT, PlotlyNode, TraceNode, write_source_py
67

78

8-
def build_validator_py(node: PlotlyNode):
9+
def get_validator_params(node: PlotlyNode, store: dict):
910
"""
10-
Build validator class source code string for a datatype PlotlyNode
11+
Get params for the validator instance for the supplied node
12+
and add them to the store.
1113
1214
Parameters
1315
----------
1416
node : PlotlyNode
1517
The datatype node (node.is_datatype must evaluate to true) for which
16-
to build the validator class
18+
to build a validator class
19+
store : dict
20+
Dictionary to store the JSON data for the validator
1721
Returns
1822
-------
19-
str
20-
String containing source code for the validator class definition
23+
None
2124
"""
22-
23-
# Validate inputs
24-
# ---------------
25+
assert isinstance(store, dict)
2526
assert node.is_datatype
2627

27-
# Initialize
28-
import_alias = "_bv"
29-
buffer = StringIO()
30-
buffer.write(CAVEAT)
31-
32-
# Imports
33-
# -------
34-
# ### Import package of the validator's superclass ###
35-
import_str = ".".join(node.name_base_validator.split(".")[:-1])
36-
buffer.write(f"import {import_str} as {import_alias}\n")
37-
38-
# Build Validator
39-
# ---------------
40-
# ### Get dict of validator's constructor params ###
41-
params = node.get_validator_params()
42-
43-
# ### Write class definition ###
44-
class_name = node.name_validator_class
28+
raw_params = node.get_validator_params()
29+
params = dict([(k, eval(v)) for k, v in raw_params.items()])
4530
superclass_name = node.name_base_validator.split(".")[-1]
46-
buffer.write(
47-
f"""
4831

49-
class {class_name}({import_alias}.{superclass_name}):
50-
def __init__(self, plotly_name={params['plotly_name']},
51-
parent_name={params['parent_name']},
52-
**kwargs):"""
53-
)
32+
key = ".".join(node.parent_path_parts + (node.name_property,))
33+
store[key] = {"params": params, "superclass": superclass_name}
5434

55-
# ### Write constructor ###
56-
buffer.write(
57-
f"""
58-
super().__init__(plotly_name, parent_name"""
59-
)
60-
61-
# Write out remaining constructor parameters
62-
for attr_name, attr_val in params.items():
63-
if attr_name in ["plotly_name", "parent_name"]:
64-
# plotly_name and parent_name are already handled
65-
continue
66-
67-
buffer.write(
68-
f""",
69-
{attr_name}=kwargs.pop('{attr_name}', {attr_val})"""
70-
)
7135

72-
buffer.write(
73-
f""",
74-
**kwargs"""
75-
)
36+
def get_data_validator_params(base_trace_node: TraceNode, store: dict):
37+
"""
38+
Add a dict of constructor params for the DataValidator to the store.
7639
77-
buffer.write(")")
40+
Parameters
41+
----------
42+
base_trace_node : TraceNode
43+
PlotlyNode that is the parent of all of the individual trace nodes
44+
store : dict
45+
Dictionary to store the JSON data for the validator
46+
Returns
47+
-------
48+
None"""
49+
assert isinstance(store, dict)
7850

79-
# ### Return buffer's string ###
80-
return buffer.getvalue()
51+
params = build_data_validator_params(base_trace_node)
52+
store["data"] = {
53+
"params": params,
54+
"superclass": "BaseDataValidator",
55+
}
8156

8257

83-
def write_validator_py(outdir, node: PlotlyNode):
58+
def write_validator_json(outdir, params: dict):
8459
"""
85-
Build validator source code and write to a file
60+
Write out a JSON serialization of the validator arguments
61+
for all validators (keyed by f"{parent_name}.{plotly_name})
62+
63+
Each validator has a "params": {kwargs} entry and
64+
a "superclass": str to indicate the class to be instantiated
8665
8766
Parameters
8867
----------
8968
outdir : str
9069
Root outdir in which the validators package should reside
91-
node : PlotlyNode
92-
The datatype node (node.is_datatype must evaluate to true) for which
93-
to build a validator class
70+
params : dict
71+
Dictionary to store the JSON data for the validator
9472
Returns
9573
-------
9674
None
9775
"""
98-
if node.is_mapped:
99-
# No validator written for mapped nodes
100-
# e.g. no validator for layout.title_font since ths is mapped to
101-
# layout.title.font
102-
return
76+
import json
10377

104-
# Generate source code
105-
# --------------------
106-
validator_source = build_validator_py(node)
78+
# Validate inputs
79+
# ---------------
80+
if not isinstance(params, dict):
81+
raise ValueError("Expected params to be a dictionary")
10782

10883
# Write file
10984
# ----------
110-
# filepath = opath.join(outdir, "validators", *node.parent_path_parts, "__init__.py")
111-
filepath = opath.join(
112-
outdir, "validators", *node.parent_path_parts, "_" + node.name_property + ".py"
113-
)
114-
115-
write_source_py(validator_source, filepath, leading_newlines=2)
85+
filepath = opath.join(outdir, "validators", "_validators.json")
86+
with open(filepath, "w") as f:
87+
f.write(json.dumps(params, indent=4))
11688

11789

11890
def build_data_validator_params(base_trace_node: TraceNode):
@@ -131,78 +103,16 @@ def build_data_validator_params(base_trace_node: TraceNode):
131103
# Get list of trace nodes
132104
# -----------------------
133105
tracetype_nodes = base_trace_node.child_compound_datatypes
134-
135-
# Build class_map_repr string
136-
# ---------------------------
137-
# This is the repr-form of a dict from trace propert name string
138-
# to the name of the trace datatype class in the graph_objs package.
139-
buffer = StringIO()
140-
buffer.write("{\n")
141-
for i, tracetype_node in enumerate(tracetype_nodes):
142-
sfx = "," if i < len(tracetype_nodes) else ""
143-
trace_name = tracetype_node.name_property
144-
trace_datatype_class = tracetype_node.name_datatype_class
145-
buffer.write(
146-
f"""
147-
'{trace_name}': '{trace_datatype_class}'{sfx}"""
148-
)
149-
150-
buffer.write(
151-
"""
152-
}"""
106+
class_strs_map = dict(
107+
[(node.name_property, node.name_datatype_class) for node in tracetype_nodes]
153108
)
154109

155-
class_map_repr = buffer.getvalue()
156-
157-
# Build params dict
158-
# -----------------
159-
params = {
160-
"class_strs_map": class_map_repr,
161-
"plotly_name": repr("data"),
162-
"parent_name": repr(""),
110+
return {
111+
"class_strs_map": class_strs_map,
112+
"plotly_name": "data",
113+
"parent_name": "",
163114
}
164115

165-
return params
166-
167-
168-
def build_data_validator_py(base_trace_node: TraceNode):
169-
"""
170-
Build source code for the DataValidator
171-
(this is the validator that inputs a list of traces)
172-
173-
Parameters
174-
----------
175-
base_trace_node : PlotlyNode
176-
PlotlyNode that is the parent of all of the individual trace nodes
177-
Returns
178-
-------
179-
str
180-
Source code string for DataValidator class
181-
"""
182-
183-
# Get constructor params
184-
# ----------------------
185-
params = build_data_validator_params(base_trace_node)
186-
187-
# Build source code
188-
# -----------------
189-
buffer = StringIO()
190-
191-
buffer.write(
192-
f"""
193-
import _plotly_utils.basevalidators
194-
195-
class DataValidator(_plotly_utils.basevalidators.BaseDataValidator):
196-
197-
def __init__(self, plotly_name={params['plotly_name']},
198-
parent_name={params['parent_name']},
199-
**kwargs):
200-
201-
super().__init__({params['class_strs_map']}, plotly_name, parent_name, **kwargs)"""
202-
)
203-
204-
return buffer.getvalue()
205-
206116

207117
def get_data_validator_instance(base_trace_node: TraceNode):
208118
"""
@@ -223,42 +133,7 @@ def get_data_validator_instance(base_trace_node: TraceNode):
223133
# We need to eval the values to convert out of the repr-form of the
224134
# params. e.g. '3' -> 3
225135
params = build_data_validator_params(base_trace_node)
226-
eval_params = {k: eval(repr_val) for k, repr_val in params.items()}
227136

228137
# Build and return BaseDataValidator instance
229138
# -------------------------------------------
230-
return _plotly_utils.basevalidators.BaseDataValidator(**eval_params)
231-
232-
233-
def write_data_validator_py(outdir, base_trace_node: TraceNode):
234-
"""
235-
Construct and write out the DataValidator
236-
(this is the validator that inputs a list of traces)
237-
238-
Parameters
239-
----------
240-
outdir : str
241-
Root outdir in which the top-level validators package should reside
242-
base_trace_node : PlotlyNode
243-
PlotlyNode that is the parent of all of the individual trace nodes
244-
Returns
245-
-------
246-
None
247-
"""
248-
# Validate inputs
249-
# ---------------
250-
if base_trace_node.node_path:
251-
raise ValueError(
252-
"Expected root trace node.\n"
253-
'Received node with path "%s"' % base_trace_node.path_str
254-
)
255-
256-
# Build Source
257-
# ------------
258-
source = build_data_validator_py(base_trace_node)
259-
260-
# Write file
261-
# ----------
262-
# filepath = opath.join(outdir, "validators", "__init__.py")
263-
filepath = opath.join(outdir, "validators", "_data.py")
264-
write_source_py(source, filepath, leading_newlines=2)
139+
return _plotly_utils.basevalidators.BaseDataValidator(**params)

0 commit comments

Comments
 (0)