text: import os
  import re
  import sys
  import logging
  
  # Pipeline imports
  from avalon import api, io, pipeline
  import avalon.fusion
  
  # Config imports
  import pype.lib as pype
  import pype.hosts.fusion.lib as fusion_lib
  
  log = logging.getLogger("Update Slap Comp")
  
  self = sys.modules[__name__]
  self._project = None
  
  
  def _format_version_folder(folder):
      """Format a version folder based on the filepath
  
      Assumption here is made that, if the path does not exists the folder
      will be "v001"
  
      Args:
          folder: file path to a folder
  
      Returns:
          str: new version folder name
      """
  
      new_version = 1
      if os.path.isdir(folder):
          re_version = re.compile("v\d+$")
          versions = [i for i in os.listdir(folder) if os.path.isdir(i)
                      and re_version.match(i)]
          if versions:
              # ensure the "v" is not included
              new_version = int(max(versions)[1:]) + 1
  
      version_folder = "v{:03d}".format(new_version)
  
      return version_folder
  
  
  def _get_work_folder(session):
      """Convenience function to get the work folder path of the current asset"""
  
      # Get new filename, create path based on asset and work template
      template_work = self._project["config"]["template"]["work"]
      work_path = pipeline._format_work_template(template_work, session)
  
      return os.path.normpath(work_path)
  
  
  def _get_fusion_instance():
      fusion = getattr(sys.modules["__main__"], "fusion", None)
      if fusion is None:
          try:
              # Support for FuScript.exe, BlackmagicFusion module for py2 only
              import BlackmagicFusion as bmf
              fusion = bmf.scriptapp("Fusion")
          except ImportError:
              raise RuntimeError("Could not find a Fusion instance")
      return fusion
  
  
  def _format_filepath(session):
  
      project = session["AVALON_PROJECT"]
      asset = session["AVALON_ASSET"]
  
      # Save updated slap comp
      work_path = _get_work_folder(session)
      walk_to_dir = os.path.join(work_path, "scenes", "slapcomp")
      slapcomp_dir = os.path.abspath(walk_to_dir)
  
      # Ensure destination exists
      if not os.path.isdir(slapcomp_dir):
          log.warning("Folder did not exist, creating folder structure")
          os.makedirs(slapcomp_dir)
  
      # Compute output path
      new_filename = "{}_{}_slapcomp_v001.comp".format(project, asset)
      new_filepath = os.path.join(slapcomp_dir, new_filename)
  
      # Create new unqiue filepath
      if os.path.exists(new_filepath):
          new_filepath = pype.version_up(new_filepath)
  
      return new_filepath
  
  
  def _update_savers(comp, session):
      """Update all savers of the current comp to ensure the output is correct
  
      Args:
          comp (object): current comp instance
          session (dict): the current Avalon session
  
      Returns:
           None
      """
  
      new_work = _get_work_folder(session)
      renders = os.path.join(new_work, "renders")
      version_folder = _format_version_folder(renders)
      renders_version = os.path.join(renders, version_folder)
  
      comp.Print("New renders to: %s\n" % renders)
  
      with avalon.fusion.comp_lock_and_undo_chunk(comp):
          savers = comp.GetToolList(False, "Saver").values()
          for saver in savers:
              filepath = saver.GetAttrs("TOOLST_Clip_Name")[1.0]
              filename = os.path.basename(filepath)
              new_path = os.path.join(renders_version, filename)
              saver["Clip"] = new_path
  
  
  def update_frame_range(comp, representations):
      """Update the frame range of the comp and render length
  
      The start and end frame are based on the lowest start frame and the highest
      end frame
  
      Args:
          comp (object): current focused comp
          representations (list) collection of dicts
  
      Returns:
          None
  
      """
  
      version_ids = [r["parent"] for r in representations]
      versions = io.find({"type": "version", "_id": {"$in": version_ids}})
      versions = list(versions)
  
      start = min(v["data"]["frameStart"] for v in versions)
      end = max(v["data"]["frameEnd"] for v in versions)
  
      fusion_lib.update_frame_range(start, end, comp=comp)
  
  
  def switch(asset_name, filepath=None, new=True):
      """Switch the current containers of the file to the other asset (shot)
  
      Args:
          filepath (str): file path of the comp file
          asset_name (str): name of the asset (shot)
          new (bool): Save updated comp under a different name
  
      Returns:
          comp path (str): new filepath of the updated comp
  
      """
  
      # If filepath provided, ensure it is valid absolute path
      if filepath is not None:
          if not os.path.isabs(filepath):
              filepath = os.path.abspath(filepath)
  
          assert os.path.exists(filepath), "%s must exist " % filepath
  
      # Assert asset name exists
      # It is better to do this here then to wait till switch_shot does it
      asset = io.find_one({"type": "asset", "name": asset_name})
      assert asset, "Could not find '%s' in the database" % asset_name
  
      # Get current project
      self._project = io.find_one({
          "type": "project",
          "name": api.Session["AVALON_PROJECT"]
      })
  
      # Go to comp
      if not filepath:
          current_comp = avalon.fusion.get_current_comp()
          assert current_comp is not None, "Could not find current comp"
      else:
          fusion = _get_fusion_instance()
          current_comp = fusion.LoadComp(filepath, quiet=True)
          assert current_comp is not None, "Fusion could not load '%s'" % filepath
  
      host = api.registered_host()
      containers = list(host.ls())
      assert containers, "Nothing to update"
  
      representations = []
      for container in containers:
          try:
              representation = fusion_lib.switch_item(container,
                                                      asset_name=asset_name)
              representations.append(representation)
          except Exception as e:
              current_comp.Print("Error in switching! %s\n" % e.message)
  
      message = "Switched %i Loaders of the %i\n" % (len(representations),
                                                     len(containers))
      current_comp.Print(message)
  
      # Build the session to switch to
      switch_to_session = api.Session.copy()
      switch_to_session["AVALON_ASSET"] = asset['name']
  
      if new:
          comp_path = _format_filepath(switch_to_session)
  
          # Update savers output based on new session
          _update_savers(current_comp, switch_to_session)
      else:
          comp_path = pype.version_up(filepath)
  
      current_comp.Print(comp_path)
  
      current_comp.Print("\nUpdating frame range")
      update_frame_range(current_comp, representations)
  
      current_comp.Save(comp_path)
  
      return comp_path
  
  
  if __name__ == '__main__':
  
      import argparse
  
      parser = argparse.ArgumentParser(description="Switch to a shot within an"
                                                   "existing comp file")
  
      parser.add_argument("--file_path",
                          type=str,
                          default=True,
                          help="File path of the comp to use")
  
      parser.add_argument("--asset_name",
                          type=str,
                          default=True,
                          help="Name of the asset (shot) to switch")
  
      args, unknown = parser.parse_args()
  
      api.install(avalon.fusion)
      switch(args.asset_name, args.file_path)
  
      sys.exit(0)
  