blob: 8edee09c2d43ed1ab16c672a7203abd7bc3ef097 [file] [log] [blame]
Masood Malekghassemi743c10c2015-06-16 18:05:27 -07001# Copyright 2015, Google Inc.
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following disclaimer
12# in the documentation and/or other materials provided with the
13# distribution.
14# * Neither the name of Google Inc. nor the names of its
15# contributors may be used to endorse or promote products derived from
16# this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30from grpc._cython._cygrpc cimport grpc
31from grpc._cython._cygrpc cimport call
32from grpc._cython._cygrpc cimport server
33
34
35class StatusCode:
36 ok = grpc.GRPC_STATUS_OK
37 cancelled = grpc.GRPC_STATUS_CANCELLED
38 unknown = grpc.GRPC_STATUS_UNKNOWN
39 invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT
40 deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED
41 not_found = grpc.GRPC_STATUS_NOT_FOUND
42 already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS
43 permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED
44 unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED
45 resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED
46 failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION
47 aborted = grpc.GRPC_STATUS_ABORTED
48 out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE
49 unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED
50 internal = grpc.GRPC_STATUS_INTERNAL
51 unavailable = grpc.GRPC_STATUS_UNAVAILABLE
52 data_loss = grpc.GRPC_STATUS_DATA_LOSS
53
54
55class CallError:
56 ok = grpc.GRPC_CALL_OK
57 error = grpc.GRPC_CALL_ERROR
58 not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER
59 not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT
60 already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED
61 already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED
62 not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED
63 already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED
64 too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS
65 invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS
66 invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA
67
68
69class CompletionType:
70 queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN
71 queue_timeout = grpc.GRPC_QUEUE_TIMEOUT
72 operation_complete = grpc.GRPC_OP_COMPLETE
73
74
75class OperationType:
76 send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA
77 send_message = grpc.GRPC_OP_SEND_MESSAGE
78 send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
79 send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
80 receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA
81 receive_message = grpc.GRPC_OP_RECV_MESSAGE
82 receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
83 receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
84
85
86cdef class Timespec:
87
88 def __cinit__(self, time):
89 if time is None:
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -070090 self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME)
Masood Malekghassemi743c10c2015-06-16 18:05:27 -070091 elif isinstance(time, float):
92 if time == float("+inf"):
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -070093 self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME)
Masood Malekghassemi743c10c2015-06-16 18:05:27 -070094 elif time == float("-inf"):
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -070095 self.c_time = grpc.gpr_inf_past(grpc.GPR_CLOCK_REALTIME)
Masood Malekghassemi743c10c2015-06-16 18:05:27 -070096 else:
97 self.c_time.seconds = time
98 self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -070099 self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700100 else:
101 raise TypeError("expected time to be float")
102
103 @property
104 def seconds(self):
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700105 # TODO(atash) ensure that everywhere a Timespec is created that it's
106 # converted to GPR_CLOCK_REALTIME then and not every time someone wants to
107 # read values off in Python.
108 cdef grpc.gpr_timespec real_time = (
109 grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
110 return real_time.seconds
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700111
112 @property
113 def nanoseconds(self):
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700114 cdef grpc.gpr_timespec real_time = (
115 grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
116 return real_time.nanoseconds
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700117
118 def __float__(self):
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700119 cdef grpc.gpr_timespec real_time = (
120 grpc.gpr_convert_clock_type(self.c_time, grpc.GPR_CLOCK_REALTIME))
121 return <double>real_time.seconds + <double>real_time.nanoseconds / 1e9
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700122
123 infinite_future = Timespec(float("+inf"))
124 infinite_past = Timespec(float("-inf"))
125
126
127cdef class CallDetails:
128
129 def __cinit__(self):
130 grpc.grpc_call_details_init(&self.c_details)
131
132 def __dealloc__(self):
133 grpc.grpc_call_details_destroy(&self.c_details)
134
135 @property
136 def method(self):
137 if self.c_details.method != NULL:
138 return <bytes>self.c_details.method
139 else:
140 return None
141
142 @property
143 def host(self):
144 if self.c_details.host != NULL:
145 return <bytes>self.c_details.host
146 else:
147 return None
148
149 @property
150 def deadline(self):
151 timespec = Timespec(float("-inf"))
152 timespec.c_time = self.c_details.deadline
153 return timespec
154
155
156cdef class OperationTag:
157
158 def __cinit__(self, user_tag):
159 self.user_tag = user_tag
160 self.references = []
161
162
163cdef class Event:
164
165 def __cinit__(self, grpc.grpc_completion_type type, bint success,
166 object tag, call.Call operation_call,
167 CallDetails request_call_details,
168 Metadata request_metadata,
169 Operations batch_operations):
170 self.type = type
171 self.success = success
172 self.tag = tag
173 self.operation_call = operation_call
174 self.request_call_details = request_call_details
175 self.request_metadata = request_metadata
176 self.batch_operations = batch_operations
177
178
179cdef class ByteBuffer:
180
181 def __cinit__(self, data):
182 if data is None:
183 self.c_byte_buffer = NULL
184 return
185 if isinstance(data, bytes):
186 pass
187 elif isinstance(data, basestring):
188 data = data.encode()
189 else:
190 raise TypeError("expected value to be of type str or bytes")
191
192 cdef char *c_data = data
193 data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data))
194 self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create(
195 &data_slice, 1)
196 grpc.gpr_slice_unref(data_slice)
197
198 def bytes(self):
199 cdef grpc.grpc_byte_buffer_reader reader
200 cdef grpc.gpr_slice data_slice
201 cdef size_t data_slice_length
202 cdef void *data_slice_pointer
203 if self.c_byte_buffer != NULL:
204 grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer)
205 result = b""
206 while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice):
207 data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice)
208 data_slice_length = grpc.gpr_slice_length(data_slice)
209 result += (<char *>data_slice_pointer)[:data_slice_length]
210 grpc.grpc_byte_buffer_reader_destroy(&reader)
211 return result
212 else:
213 return None
214
215 def __len__(self):
216 if self.c_byte_buffer != NULL:
217 return grpc.grpc_byte_buffer_length(self.c_byte_buffer)
218 else:
219 return 0
220
221 def __str__(self):
222 return self.bytes()
223
224 def __dealloc__(self):
225 if self.c_byte_buffer != NULL:
226 grpc.grpc_byte_buffer_destroy(self.c_byte_buffer)
227
228
229cdef class SslPemKeyCertPair:
230
231 def __cinit__(self, private_key, certificate_chain):
232 if isinstance(private_key, bytes):
233 self.private_key = private_key
234 elif isinstance(private_key, basestring):
235 self.private_key = private_key.encode()
236 else:
237 raise TypeError("expected private_key to be of type str or bytes")
238 if isinstance(certificate_chain, bytes):
239 self.certificate_chain = certificate_chain
240 elif isinstance(certificate_chain, basestring):
241 self.certificate_chain = certificate_chain.encode()
242 else:
243 raise TypeError("expected certificate_chain to be of type str or bytes "
244 "or int")
245 self.c_pair.private_key = self.private_key
246 self.c_pair.certificate_chain = self.certificate_chain
247
248
249cdef class ChannelArg:
250
251 def __cinit__(self, key, value):
252 if isinstance(key, bytes):
253 self.key = key
254 elif isinstance(key, basestring):
255 self.key = key.encode()
256 else:
257 raise TypeError("expected key to be of type str or bytes")
258 if isinstance(value, bytes):
259 self.value = value
260 self.c_arg.type = grpc.GRPC_ARG_STRING
261 self.c_arg.value.string = self.value
262 elif isinstance(value, basestring):
263 self.value = value.encode()
264 self.c_arg.type = grpc.GRPC_ARG_STRING
265 self.c_arg.value.string = self.value
266 elif isinstance(value, int):
267 self.value = int(value)
268 self.c_arg.type = grpc.GRPC_ARG_INTEGER
269 self.c_arg.value.integer = self.value
270 else:
271 raise TypeError("expected value to be of type str or bytes or int")
272 self.c_arg.key = self.key
273
274
275cdef class ChannelArgs:
276
277 def __cinit__(self, args):
278 self.args = list(args)
279 for arg in self.args:
280 if not isinstance(arg, ChannelArg):
281 raise TypeError("expected list of ChannelArg")
282 self.c_args.arguments_length = len(self.args)
283 self.c_args.arguments = <grpc.grpc_arg *>grpc.gpr_malloc(
284 self.c_args.arguments_length*sizeof(grpc.grpc_arg)
285 )
286 for i in range(self.c_args.arguments_length):
287 self.c_args.arguments[i] = (<ChannelArg>self.args[i]).c_arg
288
289 def __dealloc__(self):
290 grpc.gpr_free(self.c_args.arguments)
291
292 def __len__(self):
293 # self.args is never stale; it's only updated from this file
294 return len(self.args)
295
296 def __getitem__(self, size_t i):
297 # self.args is never stale; it's only updated from this file
298 return self.args[i]
299
300
301cdef class Metadatum:
302
303 def __cinit__(self, key, value):
304 if isinstance(key, bytes):
305 self._key = key
306 elif isinstance(key, basestring):
307 self._key = key.encode()
308 else:
309 raise TypeError("expected key to be of type str or bytes")
310 if isinstance(value, bytes):
311 self._value = value
312 elif isinstance(value, basestring):
313 self._value = value.encode()
314 else:
315 raise TypeError("expected value to be of type str or bytes")
316 self.c_metadata.key = self._key
317 self.c_metadata.value = self._value
318 self.c_metadata.value_length = len(self._value)
319
320 @property
321 def key(self):
322 return <bytes>self.c_metadata.key
323
324 @property
325 def value(self):
326 return <bytes>self.c_metadata.value[:self.c_metadata.value_length]
327
328 def __len__(self):
329 return 2
330
331 def __getitem__(self, size_t i):
332 if i == 0:
333 return self.key
334 elif i == 1:
335 return self.value
336 else:
337 raise IndexError("index must be 0 (key) or 1 (value)")
338
339 def __iter__(self):
340 return iter((self.key, self.value))
341
342
343cdef class _MetadataIterator:
344
345 cdef size_t i
346 cdef Metadata metadata
347
348 def __cinit__(self, Metadata metadata not None):
349 self.i = 0
350 self.metadata = metadata
351
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700352 def __iter__(self):
353 return self
354
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700355 def __next__(self):
356 if self.i < len(self.metadata):
357 result = self.metadata[self.i]
358 self.i = self.i + 1
359 return result
360 else:
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700361 raise StopIteration
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700362
363
364cdef class Metadata:
365
366 def __cinit__(self, metadata):
367 self.metadata = list(metadata)
368 for metadatum in metadata:
369 if not isinstance(metadatum, Metadatum):
370 raise TypeError("expected list of Metadatum")
371 grpc.grpc_metadata_array_init(&self.c_metadata_array)
372 self.c_metadata_array.count = len(self.metadata)
373 self.c_metadata_array.capacity = len(self.metadata)
374 self.c_metadata_array.metadata = <grpc.grpc_metadata *>grpc.gpr_malloc(
375 self.c_metadata_array.count*sizeof(grpc.grpc_metadata)
376 )
377 for i in range(self.c_metadata_array.count):
378 self.c_metadata_array.metadata[i] = (
379 (<Metadatum>self.metadata[i]).c_metadata)
380
381 def __dealloc__(self):
382 # this frees the allocated memory for the grpc_metadata_array (although
383 # it'd be nice if that were documented somewhere...) TODO(atash): document
384 # this in the C core
385 grpc.grpc_metadata_array_destroy(&self.c_metadata_array)
386
387 def __len__(self):
388 return self.c_metadata_array.count
389
390 def __getitem__(self, size_t i):
391 return Metadatum(
392 key=<bytes>self.c_metadata_array.metadata[i].key,
393 value=<bytes>self.c_metadata_array.metadata[i].value[
394 :self.c_metadata_array.metadata[i].value_length])
395
396 def __iter__(self):
397 return _MetadataIterator(self)
398
399
400cdef class Operation:
401
402 def __cinit__(self):
403 self.references = []
404 self._received_status_details = NULL
405 self._received_status_details_capacity = 0
406 self.is_valid = False
407
408 @property
409 def type(self):
410 return self.c_op.type
411
412 @property
413 def received_message(self):
414 if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE:
415 raise TypeError("self must be an operation receiving a message")
416 return self._received_message
417
418 @property
419 def received_metadata(self):
420 if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and
421 self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT):
422 raise TypeError("self must be an operation receiving metadata")
423 return self._received_metadata
424
425 @property
426 def received_status_code(self):
427 if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
428 raise TypeError("self must be an operation receiving a status code")
429 return self._received_status_code
430
431 @property
432 def received_status_details(self):
433 if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
434 raise TypeError("self must be an operation receiving status details")
435 if self._received_status_details:
436 return self._received_status_details
437 else:
438 return None
439
440 @property
441 def received_cancelled(self):
442 if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER:
443 raise TypeError("self must be an operation receiving cancellation "
444 "information")
445 return False if self._received_cancelled == 0 else True
446
447 def __dealloc__(self):
448 # We *almost* don't need to do anything; most of the objects are handled by
449 # Python. The remaining one(s) are primitive fields filled in by GRPC core.
450 # This means that we need to clean up after receive_status_on_client.
451 if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT:
452 grpc.gpr_free(self._received_status_details)
453
454def operation_send_initial_metadata(Metadata metadata):
455 cdef Operation op = Operation()
456 op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA
457 op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count
458 op.c_op.data.send_initial_metadata.metadata = (
459 metadata.c_metadata_array.metadata)
460 op.references.append(metadata)
461 op.is_valid = True
462 return op
463
464def operation_send_message(data):
465 cdef Operation op = Operation()
466 op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE
467 byte_buffer = ByteBuffer(data)
468 op.c_op.data.send_message = byte_buffer.c_byte_buffer
469 op.references.append(byte_buffer)
470 op.is_valid = True
471 return op
472
473def operation_send_close_from_client():
474 cdef Operation op = Operation()
475 op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT
476 op.is_valid = True
477 return op
478
479def operation_send_status_from_server(
480 Metadata metadata, grpc.grpc_status_code code, details):
481 if isinstance(details, bytes):
482 pass
483 elif isinstance(details, basestring):
484 details = details.encode()
485 else:
486 raise TypeError("expected a str or bytes object for details")
487 cdef Operation op = Operation()
488 op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER
489 op.c_op.data.send_status_from_server.trailing_metadata_count = (
490 metadata.c_metadata_array.count)
491 op.c_op.data.send_status_from_server.trailing_metadata = (
492 metadata.c_metadata_array.metadata)
493 op.c_op.data.send_status_from_server.status = code
494 op.c_op.data.send_status_from_server.status_details = details
495 op.references.append(metadata)
496 op.references.append(details)
497 op.is_valid = True
498 return op
499
500def operation_receive_initial_metadata():
501 cdef Operation op = Operation()
502 op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA
503 op._received_metadata = Metadata([])
504 op.c_op.data.receive_initial_metadata = (
505 &op._received_metadata.c_metadata_array)
506 op.is_valid = True
507 return op
508
509def operation_receive_message():
510 cdef Operation op = Operation()
511 op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE
512 op._received_message = ByteBuffer(None)
513 # n.b. the c_op.data.receive_message field needs to be deleted by us,
514 # anyway, so we just let that be handled by the ByteBuffer() we allocated
515 # the line before.
516 op.c_op.data.receive_message = &op._received_message.c_byte_buffer
517 op.is_valid = True
518 return op
519
520def operation_receive_status_on_client():
521 cdef Operation op = Operation()
522 op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT
523 op._received_metadata = Metadata([])
524 op.c_op.data.receive_status_on_client.trailing_metadata = (
525 &op._received_metadata.c_metadata_array)
526 op.c_op.data.receive_status_on_client.status = (
527 &op._received_status_code)
528 op.c_op.data.receive_status_on_client.status_details = (
529 &op._received_status_details)
530 op.c_op.data.receive_status_on_client.status_details_capacity = (
531 &op._received_status_details_capacity)
532 op.is_valid = True
533 return op
534
535def operation_receive_close_on_server():
536 cdef Operation op = Operation()
537 op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER
538 op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled
539 op.is_valid = True
540 return op
541
542
543cdef class _OperationsIterator:
544
545 cdef size_t i
546 cdef Operations operations
547
548 def __cinit__(self, Operations operations not None):
549 self.i = 0
550 self.operations = operations
551
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700552 def __iter__(self):
553 return self
554
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700555 def __next__(self):
556 if self.i < len(self.operations):
557 result = self.operations[self.i]
558 self.i = self.i + 1
559 return result
560 else:
Masood Malekghassemi5a65bcd2015-09-25 11:27:10 -0700561 raise StopIteration
Masood Malekghassemi743c10c2015-06-16 18:05:27 -0700562
563
564cdef class Operations:
565
566 def __cinit__(self, operations):
567 self.operations = list(operations) # normalize iterable
568 self.c_ops = NULL
569 self.c_nops = 0
570 for operation in self.operations:
571 if not isinstance(operation, Operation):
572 raise TypeError("expected operations to be iterable of Operation")
573 self.c_nops = len(self.operations)
574 self.c_ops = <grpc.grpc_op *>grpc.gpr_malloc(
575 sizeof(grpc.grpc_op)*self.c_nops)
576 for i in range(self.c_nops):
577 self.c_ops[i] = (<Operation>(self.operations[i])).c_op
578
579 def __len__(self):
580 return self.c_nops
581
582 def __getitem__(self, size_t i):
583 # self.operations is never stale; it's only updated from this file
584 return self.operations[i]
585
586 def __dealloc__(self):
587 grpc.gpr_free(self.c_ops)
588
589 def __iter__(self):
590 return _OperationsIterator(self)
591