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