def _infer_unpack_needed()

in tensorflow_federated/python/core/impl/computation/function_utils.py [0:0]


def _infer_unpack_needed(fn: types.FunctionType,
                         parameter_type: computation_types.Type,
                         should_unpack: Optional[bool] = None) -> bool:
  """Returns whether parameter_type must be unpacked when calling fn.

  Args:
    fn: The function to be invoked.
    parameter_type: The TFF type of the parameter bundle to be accepted by the
      returned callable.
    should_unpack: Default or expected return value; None implies the inferred
      value should be returned. If either unpacking or packing could work, and
      should_unpack is not None, then should_unpack is returned.

  Returns:
    A `bool` indicating whether or not to unpack.
  """
  # TODO(b/113112885): Revisit whether the 3-way 'unpack' knob is sufficient
  # for our needs, or more options are needed.
  if should_unpack not in [True, False, None]:
    raise TypeError('The unpack argument has an unexpected value {!r}.'.format(
        should_unpack))
  py_typecheck.check_type(parameter_type, computation_types.Type)
  unpack = should_unpack  # Default return value.
  signature = get_signature(fn)
  unpack_required = not is_signature_compatible_with_types(
      signature, parameter_type)
  # Boolean identity comparison becaue unpack can have a non-boolean value.
  if unpack_required and should_unpack is False:  # pylint: disable=g-bool-id-comparison
    raise TypeError(
        'The supplied function \'{}\' with signature {} cannot accept a '
        'value of type \'{}\' as a single argument.'.format(
            fn.__name__, signature, parameter_type))
  if is_argument_struct(parameter_type):
    arg_types, kwarg_types = unpack_args_from_struct(parameter_type)
    unpack_possible = is_signature_compatible_with_types(
        signature, *arg_types, **kwarg_types)
  else:
    unpack_possible = False
  # Boolean identity comparison becaue unpack can have a non-boolean value.
  if not unpack_possible and should_unpack is True:  # pylint: disable=g-bool-id-comparison
    raise TypeError(
        'The supplied function with signature {} cannot accept a value of type '
        '{} as multiple positional and/or keyword arguments. That is, the '
        'argument cannot be unpacked, but unpacking was requested.'.format(
            signature, parameter_type))
  if unpack_required and not unpack_possible:
    raise TypeError(
        'The supplied function "{}" with signature {} cannot accept a value of '
        'type {} as either a single argument or multiple positional and/or '
        'keyword arguments.'.format(fn.__name__, signature, parameter_type))
  if not unpack_required and unpack_possible and should_unpack is None:
    # The supplied function could accept a value as either a single argument,
    # or as multiple positional and/or keyword arguments, and the caller did
    # not specify any preference, leaving ambiguity in how to handle the
    # mapping. We resolve the ambiguity by defaulting to capturing the entire
    # argument, as that's the behavior suggested as expected by the users.
    unpack = False

  if unpack is None:
    # Any ambiguity at this point has been resolved, so the following
    # condition holds and need only be verified in tests.
    assert unpack_required == unpack_possible, (unpack_required,
                                                unpack_possible)
    unpack = unpack_possible

  return unpack