Skip to content

Refactor validators #5173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 82 commits into
base: release-6.1.0rc0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
8a787b5
Add deprecation notice for orca
LiamConnors Mar 25, 2025
9dd3330
Create static-image-generation-migration.md
LiamConnors Mar 25, 2025
97168bf
Add deprecation notice
LiamConnors Mar 25, 2025
ca80cdd
Add info on changes in Plotly.py 6.1
LiamConnors Mar 25, 2025
7c76ee3
Add draft of Kaleido updates
LiamConnors Mar 25, 2025
2e0b441
Add default settings info
LiamConnors Mar 27, 2025
577329d
Make small updates and add back Orca export settings
LiamConnors Mar 27, 2025
02b31f0
Update link
LiamConnors Mar 31, 2025
3281d99
Update static-image-export.md
LiamConnors Mar 31, 2025
2cbd63c
Update migration guide for Kaleido
LiamConnors Mar 31, 2025
9855b6a
remove orca section
LiamConnors Apr 1, 2025
28e7e19
Update static-image-export.md
LiamConnors Apr 1, 2025
e8149ae
Small updates
LiamConnors Apr 1, 2025
e45d56e
Merge branch 'main' into kaleido-docs-updates
LiamConnors Apr 1, 2025
0b79a17
Update static-image-generation-migration.md
LiamConnors Apr 1, 2025
f8cad81
Merge branch 'kaleido-docs-updates' of https://github.com/plotly/plot…
LiamConnors Apr 1, 2025
f5ac772
Update doc/python/static-image-export.md
LiamConnors Apr 2, 2025
10e8f20
Update doc/python/static-image-export.md
LiamConnors Apr 2, 2025
bbac70b
Update doc/python/static-image-export.md
LiamConnors Apr 2, 2025
9ad552e
Update doc/python/static-image-export.md
LiamConnors Apr 2, 2025
c290b5f
Update doc/python/static-image-export.md
LiamConnors Apr 2, 2025
7b8061b
Update static-image-generation-migration.md
LiamConnors Apr 3, 2025
07fd630
Merge branch 'kaleido-docs-updates' of https://github.com/plotly/plot…
LiamConnors Apr 3, 2025
6ba54f3
Update static-image-export.md
LiamConnors Apr 23, 2025
30cc778
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 23, 2025
9b09504
Merge branch 'main' into kaleido-docs-updates
LiamConnors Apr 28, 2025
fded494
Merge branch 'kaleido-docs-updates' of https://github.com/plotly/plot…
LiamConnors Apr 29, 2025
077a356
Add note on browsers
LiamConnors Apr 29, 2025
2821776
Merge branch 'main' into kaleido-docs-updates
LiamConnors Apr 29, 2025
9f9262b
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 30, 2025
551eaa6
update install commands
LiamConnors Apr 30, 2025
b9816bd
Merge branch 'kaleido-docs-updates' of https://github.com/plotly/plot…
LiamConnors Apr 30, 2025
8a8b052
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
fc13f1d
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 30, 2025
63229fc
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
2eeac47
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
b42185e
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 30, 2025
e753b7b
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 30, 2025
9fb73a1
Update doc/python/static-image-generation-migration.md
LiamConnors Apr 30, 2025
0284d5e
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
ff62066
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
41d72f0
Update doc/python/static-image-export.md
LiamConnors Apr 30, 2025
a27e2a9
Update static-image-export.md
LiamConnors May 1, 2025
8e62339
Update static-image-export.md
LiamConnors May 1, 2025
b8253c4
Update static-image-generation-migration.md
LiamConnors May 1, 2025
ba8ab39
Update static-image-export.md
LiamConnors May 1, 2025
2cc67f3
Update getting-started.md
LiamConnors May 1, 2025
9be1a49
Update static-image-generation-migration.md
LiamConnors May 1, 2025
70984fb
Update _kaleido.py
LiamConnors May 2, 2025
1965279
Merge pull request #5161 from plotly/release-6.1.0rc0
emilykl May 2, 2025
2f66bf1
Update plotly/io/_kaleido.py
LiamConnors May 6, 2025
815d975
Merge pull request #5164 from plotly/docstring-updates
LiamConnors May 6, 2025
d3ba372
Merge branch 'main' into kaleido-docs-updates
LiamConnors May 6, 2025
f70d8bf
Merge pull request #5111 from plotly/kaleido-docs-updates
LiamConnors May 7, 2025
5d29c69
removing old generated validators
bmaranville May 8, 2025
b359323
write single json instead of python files
bmaranville May 8, 2025
71be168
generate validator class instances from json data
bmaranville May 8, 2025
f3b546c
use validators from ValidatorCache
bmaranville May 8, 2025
646b4b6
use validators from ValidatorCache
bmaranville May 8, 2025
03100b4
use DataValidator from ValidatorCache
bmaranville May 8, 2025
0f6040b
trace validator should be instance not class of DataValidator
bmaranville May 8, 2025
2fb4670
use validators from ValidatorCache
bmaranville May 8, 2025
ae2ad4d
use template validator from ValidatorCache
bmaranville May 8, 2025
e92cf30
allow overlaying args on derived classes
bmaranville May 8, 2025
b46cb01
add DataValidator to autogenerated instances provided by ValidatorCac…
bmaranville May 8, 2025
9f20fd9
add DataValidator to autogenerated instances provided by ValidatorCac…
bmaranville May 8, 2025
c4df7e3
add DataValidator to autogenerated instances provided by ValidatorCac…
bmaranville May 8, 2025
bc2118a
use a copy of the DataValidator in BaseFigure, with local set_uid att…
bmaranville May 8, 2025
09e0140
Merge branch 'plotly:main' into refactor-validators
bmaranville May 8, 2025
d48cbe1
update validator params store
bmaranville May 8, 2025
d3c5f92
removing _data.py in validators, DataValidator is generated from Base…
bmaranville May 8, 2025
2f26c48
Merge branch 'refactor-validators' of github.com:bmaranville/plotly.p…
bmaranville May 8, 2025
00a8d1d
DataValidator is an instance already, and is not callable
bmaranville May 8, 2025
5607de0
use heatmap colorscale validator
bmaranville May 8, 2025
a23e0b7
update documentation example to use validator from ValidatorCache
bmaranville May 8, 2025
1b72556
cleanup
bmaranville May 9, 2025
689fa16
black formatting
bmaranville May 9, 2025
d893537
black formatting
bmaranville May 9, 2025
9e033b6
revert this file - it is auto-generated
bmaranville May 9, 2025
809ccc4
cleanup
bmaranville May 9, 2025
c191427
use ValidatorCache for generated Layout validators
bmaranville May 9, 2025
c301c4a
black formatting
bmaranville May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
39 changes: 12 additions & 27 deletions codegen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
build_from_imports_py,
)
from codegen.validators import (
write_validator_py,
write_data_validator_py,
get_data_validator_params,
get_validator_params,
write_validator_json,
get_data_validator_instance,
)

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

validator_params = {}
# Write out validators
# --------------------
# # ### Layout ###
for node in all_layout_nodes:
write_validator_py(outdir, node)
get_validator_params(node, validator_params)

# ### Trace ###
for node in all_trace_nodes:
write_validator_py(outdir, node)
get_validator_params(node, validator_params)

# ### Frames ###
for node in all_frame_nodes:
write_validator_py(outdir, node)
get_validator_params(node, validator_params)

# ### Data (traces) validator ###
write_data_validator_py(outdir, base_traces_node)
get_data_validator_params(base_traces_node, validator_params)

# Write out the JSON data for the validators
os.makedirs(validators_pkgdir, exist_ok=True)
write_validator_json(outdir, validator_params)

# Alls
# ----
Expand Down Expand Up @@ -217,27 +223,6 @@ def perform_codegen(reformat=True):
layout_array_nodes,
)

# Write validator __init__.py files
# ---------------------------------
# ### Write __init__.py files for each validator package ###
validator_rel_class_imports = {}
for node in all_datatype_nodes:
if node.is_mapped:
continue
key = node.parent_path_parts
validator_rel_class_imports.setdefault(key, []).append(
f"._{node.name_property}.{node.name_validator_class}"
)

# Add Data validator
root_validator_pairs = validator_rel_class_imports[()]
root_validator_pairs.append("._data.DataValidator")

# Output validator __init__.py files
validators_pkg = opath.join(outdir, "validators")
for path_parts, rel_classes in validator_rel_class_imports.items():
write_init_py(validators_pkg, path_parts, [], rel_classes)

# Write datatype __init__.py files
# --------------------------------
datatype_rel_class_imports = {}
Expand Down
9 changes: 3 additions & 6 deletions codegen/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,11 @@ class {datatype_class}(_{node.name_base_datatype}):\n"""
"""
)

subplot_validator_names = [n.name_validator_class for n in subplot_nodes]

validator_csv = ", ".join(subplot_validator_names)
subplot_dict_str = (
"{"
+ ", ".join(
f"'{subname}': {valname}"
for subname, valname in zip(subplot_names, subplot_validator_names)
f'"{subname}": ValidatorCache.get_validator("layout", "{subname}")'
for subname in subplot_names
)
+ "}"
)
Expand All @@ -153,7 +150,7 @@ def _subplotid_validators(self):
-------
dict
\"\"\"
from plotly.validators.layout import ({validator_csv})
from plotly.validator_cache import ValidatorCache

return {subplot_dict_str}

Expand Down
234 changes: 55 additions & 179 deletions codegen/validators.py
Original file line number Diff line number Diff line change
@@ -1,118 +1,91 @@
import os.path as opath
from io import StringIO
import json

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


def build_validator_py(node: PlotlyNode):
def get_validator_params(node: PlotlyNode, store: dict):
"""
Build validator class source code string for a datatype PlotlyNode
Get params for the validator instance for the supplied node
and add them to the store.

Parameters
----------
node : PlotlyNode
The datatype node (node.is_datatype must evaluate to true) for which
to build the validator class
to build a validator class
store : dict
Dictionary to store the JSON data for the validator
Returns
-------
str
String containing source code for the validator class definition
None
"""

# Validate inputs
# ---------------
assert isinstance(store, dict)
assert node.is_datatype

# Initialize
import_alias = "_bv"
buffer = StringIO()
buffer.write(CAVEAT)

# Imports
# -------
# ### Import package of the validator's superclass ###
import_str = ".".join(node.name_base_validator.split(".")[:-1])
buffer.write(f"import {import_str} as {import_alias}\n")

# Build Validator
# ---------------
# ### Get dict of validator's constructor params ###
params = node.get_validator_params()

# ### Write class definition ###
class_name = node.name_validator_class
raw_params = node.get_validator_params()
params = dict([(k, eval(v)) for k, v in raw_params.items()])
superclass_name = node.name_base_validator.split(".")[-1]
buffer.write(
f"""

class {class_name}({import_alias}.{superclass_name}):
def __init__(self, plotly_name={params['plotly_name']},
parent_name={params['parent_name']},
**kwargs):"""
)
key = ".".join(node.parent_path_parts + (node.name_property,))
store[key] = {"params": params, "superclass": superclass_name}

# ### Write constructor ###
buffer.write(
f"""
super().__init__(plotly_name, parent_name"""
)

# Write out remaining constructor parameters
for attr_name, attr_val in params.items():
if attr_name in ["plotly_name", "parent_name"]:
# plotly_name and parent_name are already handled
continue

buffer.write(
f""",
{attr_name}=kwargs.pop('{attr_name}', {attr_val})"""
)

buffer.write(
f""",
**kwargs"""
)
def get_data_validator_params(base_trace_node: TraceNode, store: dict):
"""
Add a dict of constructor params for the DataValidator to the store.

buffer.write(")")
Parameters
----------
base_trace_node : TraceNode
PlotlyNode that is the parent of all of the individual trace nodes
store : dict
Dictionary to store the JSON data for the validator
Returns
-------
None"""
assert isinstance(store, dict)

# ### Return buffer's string ###
return buffer.getvalue()
params = build_data_validator_params(base_trace_node)
store["data"] = {
"params": params,
"superclass": "BaseDataValidator",
}


def write_validator_py(outdir, node: PlotlyNode):
def write_validator_json(outdir, params: dict):
"""
Build validator source code and write to a file
Write out a JSON serialization of the validator arguments
for all validators (keyed by f"{parent_name}.{plotly_name})

Each validator has a "params": {kwargs} entry and
a "superclass": str to indicate the class to be instantiated

Parameters
----------
outdir : str
Root outdir in which the validators package should reside
node : PlotlyNode
The datatype node (node.is_datatype must evaluate to true) for which
to build a validator class
params : dict
Dictionary to store the JSON data for the validator
Returns
-------
None
"""
if node.is_mapped:
# No validator written for mapped nodes
# e.g. no validator for layout.title_font since ths is mapped to
# layout.title.font
return
import json

# Generate source code
# --------------------
validator_source = build_validator_py(node)
# Validate inputs
# ---------------
if not isinstance(params, dict):
raise ValueError("Expected params to be a dictionary")

# Write file
# ----------
# filepath = opath.join(outdir, "validators", *node.parent_path_parts, "__init__.py")
filepath = opath.join(
outdir, "validators", *node.parent_path_parts, "_" + node.name_property + ".py"
)

write_source_py(validator_source, filepath, leading_newlines=2)
filepath = opath.join(outdir, "validators", "_validators.json")
with open(filepath, "w") as f:
f.write(json.dumps(params, indent=4))
# f.write(str(params))


def build_data_validator_params(base_trace_node: TraceNode):
Expand All @@ -131,78 +104,16 @@ def build_data_validator_params(base_trace_node: TraceNode):
# Get list of trace nodes
# -----------------------
tracetype_nodes = base_trace_node.child_compound_datatypes

# Build class_map_repr string
# ---------------------------
# This is the repr-form of a dict from trace propert name string
# to the name of the trace datatype class in the graph_objs package.
buffer = StringIO()
buffer.write("{\n")
for i, tracetype_node in enumerate(tracetype_nodes):
sfx = "," if i < len(tracetype_nodes) else ""
trace_name = tracetype_node.name_property
trace_datatype_class = tracetype_node.name_datatype_class
buffer.write(
f"""
'{trace_name}': '{trace_datatype_class}'{sfx}"""
)

buffer.write(
"""
}"""
class_strs_map = dict(
[(node.name_property, node.name_datatype_class) for node in tracetype_nodes]
)

class_map_repr = buffer.getvalue()

# Build params dict
# -----------------
params = {
"class_strs_map": class_map_repr,
"plotly_name": repr("data"),
"parent_name": repr(""),
return {
"class_strs_map": class_strs_map,
"plotly_name": "data",
"parent_name": "",
}

return params


def build_data_validator_py(base_trace_node: TraceNode):
"""
Build source code for the DataValidator
(this is the validator that inputs a list of traces)

Parameters
----------
base_trace_node : PlotlyNode
PlotlyNode that is the parent of all of the individual trace nodes
Returns
-------
str
Source code string for DataValidator class
"""

# Get constructor params
# ----------------------
params = build_data_validator_params(base_trace_node)

# Build source code
# -----------------
buffer = StringIO()

buffer.write(
f"""
import _plotly_utils.basevalidators

class DataValidator(_plotly_utils.basevalidators.BaseDataValidator):

def __init__(self, plotly_name={params['plotly_name']},
parent_name={params['parent_name']},
**kwargs):

super().__init__({params['class_strs_map']}, plotly_name, parent_name, **kwargs)"""
)

return buffer.getvalue()


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

# Build and return BaseDataValidator instance
# -------------------------------------------
return _plotly_utils.basevalidators.BaseDataValidator(**eval_params)


def write_data_validator_py(outdir, base_trace_node: TraceNode):
"""
Construct and write out the DataValidator
(this is the validator that inputs a list of traces)

Parameters
----------
outdir : str
Root outdir in which the top-level validators package should reside
base_trace_node : PlotlyNode
PlotlyNode that is the parent of all of the individual trace nodes
Returns
-------
None
"""
# Validate inputs
# ---------------
if base_trace_node.node_path:
raise ValueError(
"Expected root trace node.\n"
'Received node with path "%s"' % base_trace_node.path_str
)

# Build Source
# ------------
source = build_data_validator_py(base_trace_node)

# Write file
# ----------
# filepath = opath.join(outdir, "validators", "__init__.py")
filepath = opath.join(outdir, "validators", "_data.py")
write_source_py(source, filepath, leading_newlines=2)
return _plotly_utils.basevalidators.BaseDataValidator(**params)
Loading