code: def _parse_signature(
      func: types.FunctionType,
      argspec: inspect.FullArgSpec,  # pytype: disable=module-attr
      typehints: Dict[str, Any],
  ) -> ParsedSignature:
      """Parses signature of a typehint-annotated component executor function.
  
      Args:
        func: A component executor function to be parsed.
        argspec: A `inspect.FullArgSpec` instance describing the component executor
          function. Usually obtained from `inspect.getfullargspec(func)`.
        typehints: A dictionary mapping function argument names to type hints.
          Usually obtained from `func.__annotations__`.
  
      Returns:
        A ParsedSignature.
      """
      # Extract optional arguments as dict from name to its declared optional value.
      arg_defaults = utils.extract_arg_defaults(argspec)
  
      # Parse function arguments.
      inputs = {}
      outputs = {}
      parameters = {}
      arg_formats = {}
      returned_outputs = {}
      json_typehints = {}
      return_json_typehints = {}
      for arg in argspec.args:
          arg_typehint = typehints[arg]
          # If the typehint is `Optional[T]` for a primitive type `T`, unwrap it.
          if arg_typehint in _OPTIONAL_PRIMITIVE_MAP:
              arg_typehint = _OPTIONAL_PRIMITIVE_MAP[arg_typehint]
          if isinstance(arg_typehint, annotations.InputArtifact):
              if arg_defaults.get(arg, None) is not None:
                  raise ValueError(
                      "If an input artifact is declared as an optional argument, "
                      "its default value must be `None` (got default value %r for "
                      "input argument %r of %r instead)."
                      % (arg_defaults[arg], arg, func)
                  )
              artifact_type = arg_typehint.type
              # If the typehint is InputArtifact[List[Artifact]]], unwrap it.
              if (
                  artifact_type is list
                  or get_origin(artifact_type) is list
              ):
                  artifact_type = get_args(artifact_type)[0]
                  arg_formats[
                      arg
                  ] = utils.ArgFormats.LIST_INPUT_ARTIFACTS
              else:
                  arg_formats[arg] = utils.ArgFormats.INPUT_ARTIFACT
              inputs[arg] = artifact_type
          elif isinstance(arg_typehint, annotations.OutputArtifact):
              if arg in arg_defaults:
                  raise ValueError(
                      "Output artifact of component function cannot be declared as "
                      "optional (error for argument %r of %r)."
                      % (arg, func)
                  )
              arg_formats[arg] = utils.ArgFormats.OUTPUT_ARTIFACT
              outputs[arg] = arg_typehint.type
          elif isinstance(arg_typehint, annotations.Parameter):
              utils.parse_parameter_arg(
                  arg,
                  arg_defaults,
                  arg_typehint,
                  parameters,
                  arg_formats,
              )
          elif isinstance(
              arg_typehint, annotations.BeamComponentParameter
          ):
              if arg in arg_defaults and arg_defaults[arg] is not None:
                  raise ValueError(
                      "The default value for BeamComponentParameter must be None."
                  )
              arg_formats[arg] = utils.ArgFormats.BEAM_PARAMETER
              parameters[arg] = arg_typehint.type
          elif arg_typehint in _PRIMITIVE_TO_ARTIFACT:
              if arg in arg_defaults:
                  if not (
                      arg_defaults[arg] is None
                      or isinstance(arg_defaults[arg], arg_typehint)
                  ):
                      raise ValueError(
                          "The default value for optional input value %r on function %r "
                          "must be an instance of its declared type %r or `None` (got %r "
                          "instead)"
                          % (arg, func, arg_typehint, arg_defaults[arg])
                      )
              arg_formats[arg] = utils.ArgFormats.ARTIFACT_VALUE
              inputs[arg] = _PRIMITIVE_TO_ARTIFACT[arg_typehint]
          elif json_compat.is_json_compatible(arg_typehint):
              json_typehints[arg] = arg_typehint
              arg_formats[arg] = utils.ArgFormats.ARTIFACT_VALUE
              inputs[arg] = standard_artifacts.JsonValue
          elif inspect.isclass(arg_typehint) and issubclass(
              arg_typehint, artifact.Artifact
          ):
              raise ValueError(
                  "Invalid type hint annotation for argument %r on function %r. "
                  "Argument with an artifact class typehint annotation should indicate "
                  "whether it is used as an input or output artifact by using the "
                  "`InputArtifact[ArtifactType]` or `OutputArtifact[ArtifactType]` "
                  "typehint annotations." % (arg, func)
              )
          else:
              raise ValueError(
                  "Unknown type hint annotation for argument %r on function %r"
                  % (arg, func)
              )
  
      if return_kwargs := _parse_return_type_kwargs(func, typehints):
          for arg, arg_typehint in return_kwargs.items():
              if arg_typehint in _OPTIONAL_PRIMITIVE_MAP:
                  unwrapped_typehint = _OPTIONAL_PRIMITIVE_MAP[
                      arg_typehint
                  ]
                  outputs[arg] = _PRIMITIVE_TO_ARTIFACT[
                      unwrapped_typehint
                  ]
                  returned_outputs[arg] = True
              elif arg_typehint in _PRIMITIVE_TO_ARTIFACT:
                  outputs[arg] = _PRIMITIVE_TO_ARTIFACT[arg_typehint]
                  returned_outputs[arg] = False
              elif json_compat.is_json_compatible(arg_typehint):
                  outputs[arg] = standard_artifacts.JsonValue
                  return_json_typehints[arg] = arg_typehint
                  # check if Optional
                  returned_outputs[arg] = get_origin(
                      arg_typehint
                  ) is Union and type(None) in get_args(arg_typehint)
              else:
                  raise ValueError(
                      "Unknown type hint annotation %r for returned output %r on "
                      "function %r" % (arg_typehint, arg, func)
                  )
  
      return ParsedSignature(
          inputs,
          outputs,
          parameters,
          arg_formats,
          arg_defaults,
          returned_outputs,
          json_typehints,
          return_json_typehints,
      )
  
file_path: tfx/tfx/dsl/component/experimental/function_parser.py
imports: import inspect
  import sys
  import types
  import typing
  from typing import Any, Dict, NamedTuple, Optional, Type, Union, get_args, get_origin
  from tfx.dsl.component.experimental import annotations
  from tfx.dsl.component.experimental import json_compat
  from tfx.dsl.component.experimental import utils
  from tfx.types import artifact
  from tfx.types import standard_artifacts
  import typing_extensions
label_desc: Use image node_id 4 for calling a global function with example usage: _parse_signature(func, argspec, typehints) and returns: ParsedSignature