code: def perform_codegen():
      # Set root codegen output directory
      # ---------------------------------
      # (relative to project root)
      abs_file_path = opath.realpath(__file__)
      packages_py = opath.dirname(
          opath.dirname(opath.dirname(abs_file_path))
      )
      outdir = opath.join(packages_py, "plotly", "plotly")
  
      # Delete prior codegen output
      # ---------------------------
      validators_pkgdir = opath.join(outdir, "validators")
      if opath.exists(validators_pkgdir):
          shutil.rmtree(validators_pkgdir)
  
      graph_objs_pkgdir = opath.join(outdir, "graph_objs")
      if opath.exists(graph_objs_pkgdir):
          shutil.rmtree(graph_objs_pkgdir)
  
      # plotly/datatypes is not used anymore, but was at one point so we'll
      # still delete it if we find it in case a developer is upgrading from an
      # older version
      datatypes_pkgdir = opath.join(outdir, "datatypes")
      if opath.exists(datatypes_pkgdir):
          shutil.rmtree(datatypes_pkgdir)
  
      # Load plotly schema
      # ------------------
      plot_schema_path = opath.join(
          packages_py,
          "plotly",
          "codegen",
          "resources",
          "plot-schema.json",
      )
  
      with open(plot_schema_path, "r") as f:
          plotly_schema = json.load(f)
  
      # Preprocess Schema
      # -----------------
      preprocess_schema(plotly_schema)
  
      # Build node lists
      # ----------------
      # ### TraceNode ###
      base_traces_node = TraceNode(plotly_schema)
      compound_trace_nodes = PlotlyNode.get_all_compound_datatype_nodes(
          plotly_schema, TraceNode
      )
      all_trace_nodes = PlotlyNode.get_all_datatype_nodes(
          plotly_schema, TraceNode
      )
  
      # ### LayoutNode ###
      compound_layout_nodes = (
          PlotlyNode.get_all_compound_datatype_nodes(
              plotly_schema, LayoutNode
          )
      )
      layout_node = compound_layout_nodes[0]
      all_layout_nodes = PlotlyNode.get_all_datatype_nodes(
          plotly_schema, LayoutNode
      )
  
      subplot_nodes = [
          node
          for node in layout_node.child_compound_datatypes
          if node.node_data.get("_isSubplotObj", False)
      ]
  
      layout_array_nodes = [
          node
          for node in layout_node.child_compound_datatypes
          if node.is_array_element
          and node.has_child("xref")
          and node.has_child("yref")
      ]
  
      # ### FrameNode ###
      compound_frame_nodes = PlotlyNode.get_all_compound_datatype_nodes(
          plotly_schema, FrameNode
      )
      frame_node = compound_frame_nodes[0]
      all_frame_nodes = PlotlyNode.get_all_datatype_nodes(
          plotly_schema, FrameNode
      )
  
      # ### All nodes ###
      all_datatype_nodes = (
          all_trace_nodes + all_layout_nodes + all_frame_nodes
      )
  
      all_compound_nodes = [
          node
          for node in all_datatype_nodes
          if node.is_compound
          and not isinstance(node, ElementDefaultsNode)
      ]
  
      # Write out validators
      # --------------------
      # # ### Layout ###
      for node in all_layout_nodes:
          write_validator_py(outdir, node)
  
      # ### Trace ###
      for node in all_trace_nodes:
          write_validator_py(outdir, node)
  
      # ### Frames ###
      for node in all_frame_nodes:
          write_validator_py(outdir, node)
  
      # ### Data (traces) validator ###
      write_data_validator_py(outdir, base_traces_node)
  
      # Alls
      # ----
      alls = {}
  
      # Write out datatypes
      # -------------------
      for node in all_compound_nodes:
          write_datatype_py(outdir, node)
  
      # ### Deprecated ###
      # These are deprecated legacy datatypes like graph_objs.Marker
      write_deprecated_datatypes(outdir)
  
      # Write figure class to graph_objs
      # --------------------------------
      data_validator = get_data_validator_instance(base_traces_node)
      layout_validator = layout_node.get_validator_instance()
      frame_validator = frame_node.get_validator_instance()
  
      write_figure_classes(
          outdir,
          base_traces_node,
          data_validator,
          layout_validator,
          frame_validator,
          subplot_nodes,
          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 = {}
      datatype_rel_module_imports = {}
  
      for node in all_compound_nodes:
          key = node.parent_path_parts
  
          # class import
          datatype_rel_class_imports.setdefault(key, []).append(
              f"._{node.name_undercase}.{node.name_datatype_class}"
          )
  
          # submodule import
          if node.child_compound_datatypes:
              datatype_rel_module_imports.setdefault(key, []).append(
                  f".{node.name_undercase}"
              )
  
      # ### Write plotly/graph_objs/graph_objs.py ###
      # This if for backward compatibility. It just imports everything from
      # graph_objs/__init__.py
      write_graph_objs_graph_objs(outdir)
  
      # ### Add Figure and FigureWidget ###
      root_datatype_imports = datatype_rel_class_imports[()]
      root_datatype_imports.append("._figure.Figure")
  
      # ### Add deprecations ###
      for dep_clas in DEPRECATED_DATATYPES:
          root_datatype_imports.append(f"._deprecations.{dep_clas}")
  
      optional_figure_widget_import = f"""
  if sys.version_info < (3, 7) or TYPE_CHECKING:
      try:
          import ipywidgets as _ipywidgets
          from packaging.version import Version as _Version
          if _Version(_ipywidgets.__version__) >= _Version("7.0.0"):
              from ..graph_objs._figurewidget import FigureWidget
          else:
              raise ImportError()
      except Exception:
          from ..missing_ipywidgets import FigureWidget
  else:
      __all__.append("FigureWidget")
      orig_getattr = __getattr__
      def __getattr__(import_name):
          if import_name == "FigureWidget":
              try:
                  import ipywidgets
                  from packaging.version import Version
  
                  if Version(ipywidgets.__version__) >= Version("7.0.0"):
                      from ..graph_objs._figurewidget import FigureWidget
  
                      return FigureWidget
                  else:
                      raise ImportError()
              except Exception:
                  from ..missing_ipywidgets import FigureWidget
                  return FigureWidget
  
          return orig_getattr(import_name)
  """
      # ### __all__ ###
      for path_parts, class_names in alls.items():
          if path_parts and class_names:
              filepath = opath.join(
                  outdir, "graph_objs", *path_parts, "__init__.py"
              )
              with open(filepath, "at") as f:
                  f.write(f"\n__all__ = {class_names}")
  
      # ### Output datatype __init__.py files ###
      graph_objs_pkg = opath.join(outdir, "graph_objs")
      for path_parts in datatype_rel_class_imports:
          rel_classes = sorted(datatype_rel_class_imports[path_parts])
          rel_modules = sorted(
              datatype_rel_module_imports.get(path_parts, [])
          )
          if path_parts == ():
              init_extra = optional_figure_widget_import
          else:
              init_extra = ""
          write_init_py(
              graph_objs_pkg,
              path_parts,
              rel_modules,
              rel_classes,
              init_extra,
          )
  
      # ### Output graph_objects.py alias
      graph_objects_rel_classes = [
          "..graph_objs." + rel_path.split(".")[-1]
          for rel_path in datatype_rel_class_imports[()]
      ]
      graph_objects_rel_modules = [
          "..graph_objs." + rel_module.split(".")[-1]
          for rel_module in datatype_rel_module_imports[()]
      ]
  
      graph_objects_init_source = build_from_imports_py(
          graph_objects_rel_modules,
          graph_objects_rel_classes,
          init_extra=optional_figure_widget_import,
      )
      graph_objects_path = opath.join(
          outdir, "graph_objects", "__init__.py"
      )
      os.makedirs(opath.join(outdir, "graph_objects"), exist_ok=True)
      with open(graph_objects_path, "wt") as f:
          f.write(graph_objects_init_source)
  
      # ### Run black code formatter on output directories ###
      subprocess.call(
          ["black", "--target-version=py36", validators_pkgdir]
      )
      subprocess.call(
          ["black", "--target-version=py36", graph_objs_pkgdir]
      )
      subprocess.call(
          ["black", "--target-version=py36", graph_objects_path]
      )
  
file_path: plotly.py/packages/python/plotly/codegen/__init__.py
imports: import json
  import os
  import os.path
  import shutil
  import subprocess
  from codegen.datatypes import build_datatype_py, write_datatype_py
  from codegen.compatibility import write_deprecated_datatypes, write_graph_objs_graph_objs, DEPRECATED_DATATYPES
  from codegen.figure import write_figure_classes
  from codegen.utils import TraceNode, PlotlyNode, LayoutNode, FrameNode, write_init_py, ElementDefaultsNode, build_from_imports_py
  from codegen.validators import write_validator_py, write_data_validator_py, get_data_validator_instance
label_desc: Use image node_id 2 for calling a global function with example usage: perform_codegen() without return types