33import os .path as opath
44import shutil
55import subprocess
6+ import sys
67
78from codegen .datatypes import build_datatype_py , write_datatype_py
89from codegen .compatibility import (
2627 get_data_validator_instance ,
2728)
2829
29- # Target Python version for code formatting with Black.
30- # Must be one of the values listed in pyproject.toml.
31- BLACK_TARGET_VERSIONS = "py38 py39 py310 py311 py312"
32-
3330
3431# Import notes
3532# ------------
3936# helpers that are only needed during code generation should reside in the
4037# codegen/ package, and helpers used both during code generation and at
4138# runtime should reside in the _plotly_utils/ package.
42- # ----------------------------------------------------------------------------
39+
4340def preprocess_schema (plotly_schema ):
4441 """
4542 Central location to make changes to schema before it's seen by the
4643 PlotlyNode classes
4744 """
4845
4946 # Update template
50- # ---------------
5147 layout = plotly_schema ["layout" ]["layoutAttributes" ]
5248
5349 # Create codegen-friendly template scheme
@@ -89,33 +85,41 @@ def preprocess_schema(plotly_schema):
8985 items ["colorscale" ] = items .pop ("concentrationscales" )
9086
9187
92- def perform_codegen (reformat = True ):
93- # Set root codegen output directory
94- # ---------------------------------
95- # (relative to project root)
96- abs_file_path = opath .realpath (__file__ )
97- project_root = opath .dirname (opath .dirname (abs_file_path ))
98- outdir = opath .join (project_root , "plotly" )
88+ def make_paths (outdir ):
89+ """Make various paths needed for formatting and linting."""
90+
91+ validators_dir = opath .join (outdir , "validators" )
92+ graph_objs_dir = opath .join (outdir , "graph_objs" )
93+ graph_objects_path = opath .join (outdir , "graph_objects" , "__init__.py" )
94+ return validators_dir , graph_objs_dir , graph_objects_path
95+
96+
97+ def lint_code (outdir ):
98+ """Check Python code using settings in pyproject.toml."""
99+
100+ subprocess .call (["ruff" , "check" , * make_paths (outdir )])
101+
102+
103+ def reformat_code (outdir ):
104+ """Reformat Python code using settings in pyproject.toml."""
105+
106+ subprocess .call (["ruff" , "format" , * make_paths (outdir )])
107+
108+
109+ def perform_codegen (outdir , noformat = False ):
110+ """Generate code (and possibly reformat)."""
111+
112+ # Get paths
113+ validators_dir , graph_objs_dir , graph_objects_path = make_paths (outdir )
99114
100115 # Delete prior codegen output
101- # ---------------------------
102- validators_pkgdir = opath .join (outdir , "validators" )
103- if opath .exists (validators_pkgdir ):
104- shutil .rmtree (validators_pkgdir )
105-
106- graph_objs_pkgdir = opath .join (outdir , "graph_objs" )
107- if opath .exists (graph_objs_pkgdir ):
108- shutil .rmtree (graph_objs_pkgdir )
109-
110- # plotly/datatypes is not used anymore, but was at one point so we'll
111- # still delete it if we find it in case a developer is upgrading from an
112- # older version
113- datatypes_pkgdir = opath .join (outdir , "datatypes" )
114- if opath .exists (datatypes_pkgdir ):
115- shutil .rmtree (datatypes_pkgdir )
116+ if opath .exists (validators_dir ):
117+ shutil .rmtree (validators_dir )
118+ if opath .exists (graph_objs_dir ):
119+ shutil .rmtree (graph_objs_dir )
116120
117121 # Load plotly schema
118- # ------------------
122+ project_root = opath . dirname ( outdir )
119123 plot_schema_path = opath .join (
120124 project_root , "codegen" , "resources" , "plot-schema.json"
121125 )
@@ -124,19 +128,17 @@ def perform_codegen(reformat=True):
124128 plotly_schema = json .load (f )
125129
126130 # Preprocess Schema
127- # -----------------
128131 preprocess_schema (plotly_schema )
129132
130133 # Build node lists
131- # ----------------
132- # ### TraceNode ###
134+ # TraceNode
133135 base_traces_node = TraceNode (plotly_schema )
134136 compound_trace_nodes = PlotlyNode .get_all_compound_datatype_nodes (
135137 plotly_schema , TraceNode
136138 )
137139 all_trace_nodes = PlotlyNode .get_all_datatype_nodes (plotly_schema , TraceNode )
138140
139- # ### LayoutNode ###
141+ # LayoutNode
140142 compound_layout_nodes = PlotlyNode .get_all_compound_datatype_nodes (
141143 plotly_schema , LayoutNode
142144 )
@@ -155,14 +157,14 @@ def perform_codegen(reformat=True):
155157 if node .is_array_element and node .has_child ("xref" ) and node .has_child ("yref" )
156158 ]
157159
158- # ### FrameNode ###
160+ # FrameNode
159161 compound_frame_nodes = PlotlyNode .get_all_compound_datatype_nodes (
160162 plotly_schema , FrameNode
161163 )
162164 frame_node = compound_frame_nodes [0 ]
163165 all_frame_nodes = PlotlyNode .get_all_datatype_nodes (plotly_schema , FrameNode )
164166
165- # ### All nodes ###
167+ # All nodes
166168 all_datatype_nodes = all_trace_nodes + all_layout_nodes + all_frame_nodes
167169
168170 all_compound_nodes = [
@@ -172,37 +174,34 @@ def perform_codegen(reformat=True):
172174 ]
173175
174176 # Write out validators
175- # --------------------
176- # # ### Layout ###
177+
178+ # # Layout
177179 for node in all_layout_nodes :
178180 write_validator_py (outdir , node )
179181
180- # ### Trace ###
182+ # Trace
181183 for node in all_trace_nodes :
182184 write_validator_py (outdir , node )
183185
184- # ### Frames ###
186+ # Frames
185187 for node in all_frame_nodes :
186188 write_validator_py (outdir , node )
187189
188- # ### Data (traces) validator ###
190+ # Data (traces) validator
189191 write_data_validator_py (outdir , base_traces_node )
190192
191193 # Alls
192- # ----
193194 alls = {}
194195
195196 # Write out datatypes
196- # -------------------
197197 for node in all_compound_nodes :
198198 write_datatype_py (outdir , node )
199199
200- # ### Deprecated ###
200+ # Deprecated
201201 # These are deprecated legacy datatypes like graph_objs.Marker
202202 write_deprecated_datatypes (outdir )
203203
204204 # Write figure class to graph_objs
205- # --------------------------------
206205 data_validator = get_data_validator_instance (base_traces_node )
207206 layout_validator = layout_node .get_validator_instance ()
208207 frame_validator = frame_node .get_validator_instance ()
@@ -218,8 +217,7 @@ def perform_codegen(reformat=True):
218217 )
219218
220219 # Write validator __init__.py files
221- # ---------------------------------
222- # ### Write __init__.py files for each validator package ###
220+ # Write __init__.py files for each validator package
223221 validator_rel_class_imports = {}
224222 for node in all_datatype_nodes :
225223 if node .is_mapped :
@@ -239,7 +237,6 @@ def perform_codegen(reformat=True):
239237 write_init_py (validators_pkg , path_parts , [], rel_classes )
240238
241239 # Write datatype __init__.py files
242- # --------------------------------
243240 datatype_rel_class_imports = {}
244241 datatype_rel_module_imports = {}
245242
@@ -257,16 +254,16 @@ def perform_codegen(reformat=True):
257254 f".{ node .name_undercase } "
258255 )
259256
260- # ### Write plotly/graph_objs/graph_objs.py ###
261- # This if for backward compatibility. It just imports everything from
257+ # Write plotly/graph_objs/graph_objs.py
258+ # This is for backward compatibility. It just imports everything from
262259 # graph_objs/__init__.py
263260 write_graph_objs_graph_objs (outdir )
264261
265- # ### Add Figure and FigureWidget ###
262+ # Add Figure and FigureWidget
266263 root_datatype_imports = datatype_rel_class_imports [()]
267264 root_datatype_imports .append ("._figure.Figure" )
268265
269- # ### Add deprecations ###
266+ # Add deprecations
270267 for dep_clas in DEPRECATED_DATATYPES :
271268 root_datatype_imports .append (f"._deprecations.{ dep_clas } " )
272269
@@ -302,14 +299,14 @@ def __getattr__(import_name):
302299
303300 return orig_getattr(import_name)
304301"""
305- # ### __all__ ###
302+ # __all__
306303 for path_parts , class_names in alls .items ():
307304 if path_parts and class_names :
308305 filepath = opath .join (outdir , "graph_objs" , * path_parts , "__init__.py" )
309306 with open (filepath , "at" ) as f :
310307 f .write (f"\n __all__ = { class_names } " )
311308
312- # ### Output datatype __init__.py files ###
309+ # Output datatype __init__.py files
313310 graph_objs_pkg = opath .join (outdir , "graph_objs" )
314311 for path_parts in datatype_rel_class_imports :
315312 rel_classes = sorted (datatype_rel_class_imports [path_parts ])
@@ -320,7 +317,7 @@ def __getattr__(import_name):
320317 init_extra = ""
321318 write_init_py (graph_objs_pkg , path_parts , rel_modules , rel_classes , init_extra )
322319
323- # ### Output graph_objects.py alias
320+ # Output graph_objects.py alias
324321 graph_objects_rel_classes = [
325322 "..graph_objs." + rel_path .split ("." )[- 1 ]
326323 for rel_path in datatype_rel_class_imports [()]
@@ -335,22 +332,19 @@ def __getattr__(import_name):
335332 graph_objects_rel_classes ,
336333 init_extra = optional_figure_widget_import ,
337334 )
338- graph_objects_path = opath .join (outdir , "graph_objects" , "__init__.py" )
339335 os .makedirs (opath .join (outdir , "graph_objects" ), exist_ok = True )
340336 with open (graph_objects_path , "wt" ) as f :
341337 f .write (graph_objects_init_source )
342338
343- # ### Run black code formatter on output directories ###
344- if reformat :
345- target_version = [
346- f"--target-version={ v } " for v in BLACK_TARGET_VERSIONS .split ()
347- ]
348- subprocess .call (["black" , * target_version , validators_pkgdir ])
349- subprocess .call (["black" , * target_version , graph_objs_pkgdir ])
350- subprocess .call (["black" , * target_version , graph_objects_path ])
351- else :
339+ # Run black code formatter on output directories
340+ if noformat :
352341 print ("skipping reformatting" )
342+ else :
343+ reformat_code (outdir )
353344
354345
355346if __name__ == "__main__" :
356- perform_codegen ()
347+ if len (sys .argv ) != 2 :
348+ print ("Usage: codegen [dirname]" , file = sys .stderr )
349+ sys .exit (1 )
350+ perform_codegen (sys .argv [1 ])
0 commit comments