def _cancel_batch(batch_fn, cancel_fn, ops):
<EOL>canceled = []<EOL>failed = []<EOL>def handle_cancel_response(request_id, response, exception):<EOL><INDENT>"""<STR_LIT>"""<EOL>del response  <EOL>if exception:<EOL><INDENT>msg = '<STR_LIT>' % (exception.resp.status, exception.resp.reason)<EOL>if exception.resp.status == FAILED_PRECONDITION_CODE:<EOL><INDENT>detail = json.loads(exception.content)<EOL>status = detail.get('<STR_LIT:error>', {}).get('<STR_LIT:status>')<EOL>if status == FAILED_PRECONDITION_STATUS:<EOL><INDENT>msg = '<STR_LIT>'<EOL><DEDENT><DEDENT>failed.append({'<STR_LIT:name>': request_id, '<STR_LIT>': msg})<EOL><DEDENT>else:<EOL><INDENT>canceled.append({'<STR_LIT:name>': request_id})<EOL><DEDENT>return<EOL><DEDENT>batch = batch_fn(callback=handle_cancel_response)<EOL>ops_by_name = {}<EOL>for op in ops:<EOL><INDENT>op_name = op.get_field('<STR_LIT>')<EOL>ops_by_name[op_name] = op<EOL>batch.add(cancel_fn(name=op_name, body={}), request_id=op_name)<EOL><DEDENT>batch.execute()<EOL>canceled_ops = [ops_by_name[op['<STR_LIT:name>']] for op in canceled]<EOL>error_messages = []<EOL>for fail in failed:<EOL><INDENT>op = ops_by_name[fail['<STR_LIT:name>']]<EOL>error_messages.append("<STR_LIT>" %<EOL>(get_operation_full_job_id(op), fail['<STR_LIT>']))<EOL><DEDENT>return canceled_ops, error_messages<EOL>
Cancel a batch of operations.

    Args:
      batch_fn: API-specific batch function.
      cancel_fn: API-specific cancel function.
      ops: A list of operations to cancel.

    Returns:
      A list of operations canceled and a list of error messages.