class_docstr: This class helps collect `MetaflowCardComponent`s during runtime execution
  
  ### Usage with `current`
  `current.card` is of type `CardComponentCollector`
  
  ### Main Usage TLDR
  - [x] `current.card.append` customizes the default editable card.
  - [x] Only one card can be default editable in a step.
  - [x] The card class must have `ALLOW_USER_COMPONENTS=True` to be considered default editable.
      - [x] Classes with `ALLOW_USER_COMPONENTS=False` are never default editable.
  - [x] The user can specify an `id` argument to a card, in which case the card is editable through `current.card[id].append`.
      - [x] A card with an id can be also default editable, if there are no other cards that are eligible to be default editable.
  - [x] If multiple default-editable cards exist but only one card doesn't have an id, the card without an id is considered to be default editable.
  - [x] If we can't resolve a single default editable card through the above rules, `current.card`.append calls show a warning but the call doesn't fail.
  - [x] A card that is not default editable can be still edited through:
      - [x] its `current.card['myid']`
      - [x] by looking it up by its type, e.g. `current.card.get(type='pytorch')`.
class_docstr_tok: ["This","class","helps","collect","`","MetaflowCardComponent","`","s","during","runtime","execution","#","#","#","Usage","with","`","current","`","`","current.card","`","is","of","type","`","CardComponentCollector","`","#","#","#","Main","Usage","TLDR","-","[","x","]","`","current.card.append","`","customizes","the","default","editable","card",".","-","[","x","]","Only","one","card","can","be","default","editable","in","a","step",".","-","[","x","]","The","card","class","must","have","`","ALLOW_USER_COMPONENTS=True","`","to","be","considered","default","editable",".","-","[","x","]","Classes","with","`","ALLOW_USER_COMPONENTS=False","`","are","never","default","editable",".","-","[","x","]","The","user","can","specify","an","`","id","`","argument","to","a","card",",","in","which","case","the","card","is","editable","through","`","current.card","[","id","]",".append","`",".","-","[","x","]","A","card","with","an","id","can","be","also","default","editable",",","if","there","are","no","other","cards","that","are","eligible","to","be","default","editable",".","-","[","x","]","If","multiple","default-editable","cards","exist","but","only","one","card","does","n't","have","an","id",",","the","card","without","an","id","is","considered","to","be","default","editable",".","-","[","x","]","If","we","ca","n't","resolve","a","single","default","editable","card","through","the","above","rules",",","`","current.card","`",".append","calls","show","a","warning","but","the","call","does","n't","fail",".","-","[","x","]","A","card","that","is","not","default","editable","can","be","still","edited","through",":","-","[","x","]","its","`","current.card","[","'myid","'","]","`","-","[","x","]","by","looking","it","up","by","its","type",",","e.g",".","`","current.card.get","(","type='pytorch","'",")","`","."]
docstr: The `_finalize` function is called once the last @card decorator calls `step_init`. Calling this function makes `current.card` ready for usage inside `@step` code.
  This function's works two parts :
  1. Resolving `self._default_editable_card`.
          - The `self._default_editable_card` holds the uuid of the card that will have access to the `append`/`extend` methods.
  2. Resolving edge cases where @card `id` argument may be `None` or have a duplicate `id` when there are more than one editable cards.
  3. Resolving the `self._default_editable_card` to the card with the`customize=True` argument.
docstr_tok: ["The","`","_finalize","`","function","is","called","once","the","last","@","card","decorator","calls","`","step_init","`",".","Calling","this","function","makes","`","current.card","`","ready","for","usage","inside","`","@","step","`","code",".","This","function","'s","works","two","parts",":","1",".","Resolving","`","self._default_editable_card","`",".","-","The","`","self._default_editable_card","`","holds","the","uuid","of","the","card","that","will","have","access","to","the","`","append","`","\/","`","extend","`","methods",".","2",".","Resolving","edge","cases","where","@","card","`","id","`","argument","may","be","`","None","`","or","have","a","duplicate","`","id","`","when","there","are","more","than","one","editable","cards",".","3",".","Resolving","the","`","self._default_editable_card","`","to","the","card","with","the","`","customize=True","`","argument","."]
code: def _finalize(self):
      """
      The `_finalize` function is called once the last @card decorator calls `step_init`. Calling this function makes `current.card` ready for usage inside `@step` code.
      This function's works two parts :
      1. Resolving `self._default_editable_card`.
              - The `self._default_editable_card` holds the uuid of the card that will have access to the `append`/`extend` methods.
      2. Resolving edge cases where @card `id` argument may be `None` or have a duplicate `id` when there are more than one editable cards.
      3. Resolving the `self._default_editable_card` to the card with the`customize=True` argument.
      """
      all_card_meta = list(self._cards_meta.values())
      for c in all_card_meta:
          ct = get_card_class(c["type"])
          c["exists"] = False
          if ct is not None:
              c["exists"] = True
  
      # If a card has `customize=True` and is not editable then it will not be considered default editable.
      editable_cards_meta = [c for c in all_card_meta if c["editable"]]
  
      if len(editable_cards_meta) == 0:
          return
  
      # Create the `self._card_id_map` lookup table which maps card `id` to `uuid`.
      # This table has access to all cards with `id`s set to them.
      card_ids = []
      for card_meta in all_card_meta:
          if card_meta["card_id"] is not None:
              self._card_id_map[card_meta["card_id"]] = card_meta[
                  "uuid"
              ]
              card_ids.append(card_meta["card_id"])
  
      # If there is only one editable card then this card becomes `self._default_editable_card`
      if len(editable_cards_meta) == 1:
          self._default_editable_card = editable_cards_meta[0]["uuid"]
          return
  
      # Segregate cards which have id as none and those which don't.
      not_none_id_cards = [
          c for c in editable_cards_meta if c["card_id"] is not None
      ]
      none_id_cards = [
          c for c in editable_cards_meta if c["card_id"] is None
      ]
  
      # If there is only 1 card with id set to None then we can use that as the default card.
      if len(none_id_cards) == 1:
          self._default_editable_card = none_id_cards[0]["uuid"]
  
      # If the size of the set of ids is not equal to total number of cards with ids then warn the user that we cannot disambiguate
      # so `current.card['my_card_id']` won't work.
      id_set = set(card_ids)
      if len(card_ids) != len(id_set):
          non_unique_ids = [
              idx
              for idx in id_set
              if len(
                  list(
                      filter(
                          lambda x: x["card_id"] == idx,
                          not_none_id_cards,
                      )
                  )
              )
              > 1
          ]
          nui = ", ".join(non_unique_ids)
          # throw a warning that decorators have non-unique Ids
          self._warning(
              (
                  "Multiple `@card` decorator have been annotated with duplicate ids : %s. "
                  "`current.card['%s']` will not work"
              )
              % (nui, non_unique_ids[0])
          )
  
          # remove the non unique ids from the `self._card_id_map`
          for idx in non_unique_ids:
              del self._card_id_map[idx]
  
      # if a @card has `customize=True` in the arguments then there should only be one @card with `customize=True`. This @card will be the _default_editable_card
      customize_cards = [
          c for c in editable_cards_meta if c["customize"]
      ]
      if len(customize_cards) > 1:
          self._warning(
              (
                  "Multiple @card decorators have `customize=True`. "
                  "Only one @card per @step can have `customize=True`. "
                  "`current.card.append` will ignore all decorators marked `customize=True`."
              )
          )
      elif len(customize_cards) == 1:
          # since `editable_cards_meta` hold only `editable=True` by default we can just set this card here.
          self._default_editable_card = customize_cards[0]["uuid"]
  
imports: from .card_modules import MetaflowCardComponent
  from .card_modules.basic import ErrorComponent, SectionComponent
  from .card_modules.components import UserComponent, create_component_id, StubComponent
  from .exception import ComponentOverwriteNotSupportedException
  from metaflow.metaflow_config import RUNTIME_CARD_RENDER_INTERVAL
  import uuid
  import json
  import platform
  from collections import OrderedDict
  import time
label_desc: Use image node_id 8 for calling the CardComponentCollector obj's underlying member method code with example usage: obj._finalize() without return types