class_docstr: An interface which endows module with neural types
class_docstr_tok: ["An","interface","which","endows","module","with","neural","types"]
docstr: This function does a few things.
  
  1) It ensures that len(out_object) == len(self.output_types).
  2) If the output is a tensor (or list/tuple of list/tuple ... of tensors), it
      attaches a neural_type to it. For objects without the neural_type attribute,
      such as python objects (dictionaries and lists, primitive data types, structs),
      no neural_type is attached.
  
  Note: tensor.neural_type is only checked during _validate_input_types which is
  called prior to forward().
  
  Args:
      output_types: Either the `output_types` defined at class level, or the local function
          overridden type definition.
      ignore_collections: For backward compatibility, container support can be disabled explicitly
          using this flag. When set to True, all nesting is ignored and nest-depth checks are skipped.
      out_objects: The outputs of the wrapped function.
docstr_tok: ["This","function","does","a","few","things",".","1",")","It","ensures","that","len","(","out_object",")","==","len","(","self.output_types",")",".","2",")","If","the","output","is","a","tensor","(","or","list\/tuple","of","list\/tuple","...","of","tensors",")",",","it","attaches","a","neural_type","to","it",".","For","objects","without","the","neural_type","attribute",",","such","as","python","objects","(","dictionaries","and","lists",",","primitive","data","types",",","structs",")",",","no","neural_type","is","attached",".","Note",":","tensor.neural_type","is","only","checked","during","_validate_input_types","which","is","called","prior","to","forward","(",")",".","Args",":","output_types",":","Either","the","`","output_types","`","defined","at","class","level",",","or","the","local","function","overridden","type","definition",".","ignore_collections",":","For","backward","compatibility",",","container","support","can","be","disabled","explicitly","using","this","flag",".","When","set","to","True",",","all","nesting","is","ignored","and","nest-depth","checks","are","skipped",".","out_objects",":","The","outputs","of","the","wrapped","function","."]
code: def _attach_and_validate_output_types(
      self, out_objects, ignore_collections=False, output_types=None
  ):
      """
      This function does a few things.
  
      1) It ensures that len(out_object) == len(self.output_types).
      2) If the output is a tensor (or list/tuple of list/tuple ... of tensors), it
          attaches a neural_type to it. For objects without the neural_type attribute,
          such as python objects (dictionaries and lists, primitive data types, structs),
          no neural_type is attached.
  
      Note: tensor.neural_type is only checked during _validate_input_types which is
      called prior to forward().
  
      Args:
          output_types: Either the `output_types` defined at class level, or the local function
              overridden type definition.
          ignore_collections: For backward compatibility, container support can be disabled explicitly
              using this flag. When set to True, all nesting is ignored and nest-depth checks are skipped.
          out_objects: The outputs of the wrapped function.
      """
      # TODO: Properly implement this
      if output_types is not None:
          # Precompute metadata
          metadata = TypecheckMetadata(
              original_types=output_types,
              ignore_collections=ignore_collections,
          )
          out_types_list = list(metadata.base_types.items())
          mandatory_out_types_list = list(
              metadata.mandatory_types.items()
          )
  
          # First convert all outputs to list/tuple format to check correct number of outputs
          if isinstance(out_objects, (list, tuple)):
              out_container = (
                  out_objects  # can be any rank nested structure
              )
          else:
              out_container = [out_objects]
  
          # If this neural type has a *single output*, with *support for nested outputs*,
          # then *do not* perform any check on the number of output items against the number
          # of neural types (in this case, 1).
          # This is done as python will *not* wrap a single returned list into a tuple of length 1,
          # instead opting to keep the list intact. Therefore len(out_container) in such a case
          # is the length of all the elements of that list - each of which has the same corresponding
          # neural type (defined as the singular container type).
          if metadata.is_singular_container_type:
              pass
  
          # In all other cases, python will wrap multiple outputs into an outer tuple.
          # Allow number of output arguments to be <= total output neural types and >= mandatory outputs.
  
          elif len(out_container) > len(out_types_list) or len(
              out_container
          ) < len(mandatory_out_types_list):
              raise TypeError(
                  "Number of output arguments provided ({}) is not as expected. "
                  "It should be larger or equal than {} and less or equal than {}.\n"
                  "This can be either because insufficient/extra number of output NeuralTypes were provided,"
                  "or the provided NeuralTypes {} should enable container support "
                  "(add '[]' to the NeuralType definition)".format(
                      len(out_container),
                      len(out_types_list),
                      len(mandatory_out_types_list),
                      output_types,
                  )
              )
  
          # Attach types recursively, if possible
          if not isinstance(out_objects, tuple) and not isinstance(
              out_objects, list
          ):
              # Here, out_objects is a single object which can potentially be attached with a NeuralType
              try:
                  out_objects.neural_type = out_types_list[0][1]
              except Exception:
                  pass
  
              # Perform output ndim check
              if hasattr(out_objects, "shape"):
                  value_shape = out_objects.shape
                  type_shape = out_types_list[0][1].axes
                  name = out_types_list[0][0]
  
                  if type_shape is not None and len(value_shape) != len(
                      type_shape
                  ):
                      raise TypeError(
                          f"Output shape mismatch occured for {name} in module {self.__class__.__name__} : \n"
                          f"Output shape expected = {type_shape} | \n"
                          f"Output shape found : {value_shape}"
                      )
  
          elif metadata.is_singular_container_type:
              # If only a single neural type is provided, and it defines a container nest,
              # then all elements of the returned list/tuple are assumed to belong to that
              # singular neural type.
              # As such, the "current" depth inside the DFS loop is counted as 1,
              # and subsequent nesting will increase this count.
  
              # NOTE:
              # As the flag `is_singular_container_type` will activate only for
              # the case where there is 1 output type defined with container nesting,
              # this is a safe assumption to make.
              depth = 1
  
              # NOTE:
              # A user may chose to explicitly wrap the single output list within an explicit tuple
              # In such a case we reduce the "current" depth to 0 - to acknowledge the fact that
              # the actual nest exists within a wrapper tuple.
              if len(out_objects) == 1 and type(out_objects) == tuple:
                  depth = 0
  
              for ind, res in enumerate(out_objects):
                  self.__attach_neural_type(
                      res,
                      metadata,
                      depth=depth,
                      name=out_types_list[0][0],
                  )
          else:
              # If more then one item is returned in a return statement, python will wrap
              # the output with an outer tuple. Therefore there must be a 1:1 correspondence
              # of the output_neural type (with or without nested structure) to the actual output
              # (whether it is a single object or a nested structure of objects).
              # Therefore in such a case, we "start" the DFS at depth 0 - since the recursion is
              # being applied on 1 neural type : 1 output struct (single or nested output).
              # Since we are guarenteed that the outer tuple will be built by python,
              # assuming initial depth of 0 is appropriate.
              for ind, res in enumerate(out_objects):
                  self.__attach_neural_type(
                      res,
                      metadata,
                      depth=0,
                      name=out_types_list[ind][0],
                  )
  
imports: import hashlib
  import inspect
  import traceback
  from abc import ABC, abstractmethod
  from contextlib import contextmanager
  from dataclasses import dataclass, field
  from enum import Enum
  from functools import total_ordering
  from pathlib import Path
  from typing import Dict, Iterable, List, Optional, Tuple, Union
  import hydra
  import torch
  import wrapt
  from huggingface_hub import HfApi, HfFolder, ModelFilter, hf_hub_download
  from huggingface_hub.hf_api import ModelInfo
  from omegaconf import DictConfig, OmegaConf
  import nemo
  from nemo.core.connectors.save_restore_connector import SaveRestoreConnector
  from nemo.core.neural_types import NeuralType, NeuralTypeComparisonResult
  from nemo.utils import logging
  from nemo.utils.cloud import maybe_download_from_cloud
  from nemo.utils.data_utils import resolve_cache_dir
  from nemo.utils.model_utils import import_class_by_path, maybe_update_config_version
label_desc: Use image node_id 4 for calling the Typing obj's underlying member method code with example usage: obj._attach_and_validate_output_types(out_objects, ignore_collections, output_types) without return types