code: def arg_type(arg_names, kwargs):
    
    assert isinstance(arg_names, tuple)
    passed = tuple(name in kwargs for name in arg_names)
    passed_and_not_none = []
    for name in arg_names:
      if name in kwargs:
        passed_and_not_none.append(kwargs[name] is not None)
      else:
        passed_and_not_none.append(False)
    passed_and_not_none = tuple(passed_and_not_none)
    dtypes = []
    for name in arg_names:
      if name not in kwargs:
        dtypes.append(None)
        continue
      value = kwargs[name]
      if value is None:
        dtypes.append(None)
        continue
      assert hasattr(value, 'dtype'), type(value)
      dtype = value.dtype
      if not isinstance(dtype, np.dtype):
        dtype = dtype.as_np_dtype
      assert isinstance(dtype, np.dtype)
      dtypes.append(dtype)
    dtypes = tuple(dtypes)
    return (passed, passed_and_not_none, dtypes)
docs: Returns a hashable summary of the types of arg_names within kwargs.
    :param arg_names: tuple containing names of relevant arguments
    :param kwargs: dict mapping string argument names to values.
      These must be values for which we can create a tf placeholder.
      Currently supported: numpy darray or something that can ducktype it
    returns:
      API contract is to return a hashable object describing all
      structural consequences of argument values that can otherwise
      be fed into a graph of fixed structure.
      Currently this is implemented as a tuple of tuples that track:
        - whether each argument was passed
        - whether each argument was passed and not None
        - the dtype of each argument
      Callers shouldn't rely on the exact structure of this object,
      just its hashability and one-to-one mapping between graph structures.