1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16cdef class Call:
17
18  def __cinit__(self):
19    # Create an *empty* call
20    fork_handlers_and_grpc_init()
21    self.c_call = NULL
22    self.references = []
23
24  def _start_batch(self, operations, tag, retain_self):
25    if not self.is_valid:
26      raise ValueError("invalid call object cannot be used from Python")
27    cdef _BatchOperationTag batch_operation_tag = _BatchOperationTag(
28        tag, operations, self if retain_self else None)
29    batch_operation_tag.prepare()
30    cpython.Py_INCREF(batch_operation_tag)
31    cdef grpc_call_error error
32    with nogil:
33      error = grpc_call_start_batch(
34          self.c_call, batch_operation_tag.c_ops, batch_operation_tag.c_nops,
35          <cpython.PyObject *>batch_operation_tag, NULL)
36    return error
37
38  def start_client_batch(self, operations, tag):
39    # We don't reference this call in the operations tag because
40    # it should be cancelled when it goes out of scope
41    return self._start_batch(operations, tag, False)
42
43  def start_server_batch(self, operations, tag):
44    return self._start_batch(operations, tag, True)
45
46  def cancel(
47      self, grpc_status_code error_code=GRPC_STATUS__DO_NOT_USE,
48      details=None):
49    details = str_to_bytes(details)
50    if not self.is_valid:
51      raise ValueError("invalid call object cannot be used from Python")
52    if (details is None) != (error_code == GRPC_STATUS__DO_NOT_USE):
53      raise ValueError("if error_code is specified, so must details "
54                       "(and vice-versa)")
55    cdef grpc_call_error result
56    cdef char *c_details = NULL
57    if error_code != GRPC_STATUS__DO_NOT_USE:
58      self.references.append(details)
59      c_details = details
60      with nogil:
61        result = grpc_call_cancel_with_status(
62            self.c_call, error_code, c_details, NULL)
63      return result
64    else:
65      with nogil:
66        result = grpc_call_cancel(self.c_call, NULL)
67      return result
68
69  def set_credentials(self, CallCredentials call_credentials not None):
70    cdef grpc_call_credentials *c_call_credentials = call_credentials.c()
71    cdef grpc_call_error call_error = grpc_call_set_credentials(
72        self.c_call, c_call_credentials)
73    grpc_call_credentials_release(c_call_credentials)
74    return call_error
75
76  def peer(self):
77    cdef char *peer = NULL
78    with nogil:
79      peer = grpc_call_get_peer(self.c_call)
80    result = <bytes>peer
81    with nogil:
82      gpr_free(peer)
83    return result
84
85  def __dealloc__(self):
86    with nogil:
87      if self.c_call != NULL:
88        grpc_call_unref(self.c_call)
89      grpc_shutdown()
90
91  # The object *should* always be valid from Python. Used for debugging.
92  @property
93  def is_valid(self):
94    return self.c_call != NULL
95
96  def _custom_op_on_c_call(self, int op):
97    return _custom_op_on_c_call(op, self.c_call)
98