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