Move batch API exposure to Python layer

Exposes the C core batch API to the Python layers. Provides a shim to
enable the old Python API to remain the same (for now).
pull/1558/head
Masood Malekghassemi 10 years ago
parent 4bbc821637
commit a2918d2e04
  1. 86
      src/python/src/grpc/_adapter/_c.c
  2. 30
      src/python/src/grpc/_adapter/_c/module.c
  3. 35
      src/python/src/grpc/_adapter/_c/types.c
  4. 271
      src/python/src/grpc/_adapter/_c/types.h
  5. 163
      src/python/src/grpc/_adapter/_c/types/call.c
  6. 134
      src/python/src/grpc/_adapter/_c/types/channel.c
  7. 286
      src/python/src/grpc/_adapter/_c/types/client_credentials.c
  8. 124
      src/python/src/grpc/_adapter/_c/types/completion_queue.c
  9. 183
      src/python/src/grpc/_adapter/_c/types/server.c
  10. 146
      src/python/src/grpc/_adapter/_c/types/server_credentials.c
  11. 460
      src/python/src/grpc/_adapter/_c/utility.c
  12. 188
      src/python/src/grpc/_adapter/_c_test.py
  13. 438
      src/python/src/grpc/_adapter/_call.c
  14. 77
      src/python/src/grpc/_adapter/_call.h
  15. 135
      src/python/src/grpc/_adapter/_channel.c
  16. 49
      src/python/src/grpc/_adapter/_channel.h
  17. 121
      src/python/src/grpc/_adapter/_client_credentials.c
  18. 49
      src/python/src/grpc/_adapter/_client_credentials.h
  19. 653
      src/python/src/grpc/_adapter/_completion_queue.c
  20. 86
      src/python/src/grpc/_adapter/_datatypes.py
  21. 79
      src/python/src/grpc/_adapter/_error.c
  22. 42
      src/python/src/grpc/_adapter/_error.h
  23. 258
      src/python/src/grpc/_adapter/_intermediary_low.py
  24. 421
      src/python/src/grpc/_adapter/_intermediary_low_test.py
  25. 104
      src/python/src/grpc/_adapter/_low.py
  26. 485
      src/python/src/grpc/_adapter/_low_test.py
  27. 202
      src/python/src/grpc/_adapter/_server.c
  28. 152
      src/python/src/grpc/_adapter/_server_credentials.c
  29. 49
      src/python/src/grpc/_adapter/_server_credentials.h
  30. 65
      src/python/src/grpc/_adapter/_tag.c
  31. 70
      src/python/src/grpc/_adapter/_tag.h
  32. 368
      src/python/src/grpc/_adapter/_types.py
  33. 4
      src/python/src/grpc/_adapter/fore.py
  34. 4
      src/python/src/grpc/_adapter/rear.py
  35. 18
      src/python/src/setup.py
  36. 19
      tools/run_tests/python_tests.json

@ -1,86 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_completion_queue.h"
#include "grpc/_adapter/_channel.h"
#include "grpc/_adapter/_call.h"
#include "grpc/_adapter/_server.h"
#include "grpc/_adapter/_client_credentials.h"
#include "grpc/_adapter/_server_credentials.h"
static PyObject *init(PyObject *self) {
grpc_init();
Py_RETURN_NONE;
}
static PyObject *shutdown(PyObject *self) {
grpc_shutdown();
Py_RETURN_NONE;
}
static PyMethodDef _c_methods[] = {
{"init", (PyCFunction)init, METH_NOARGS,
"Initialize the module's static state."},
{"shut_down", (PyCFunction)shutdown, METH_NOARGS,
"Shut down the module's static state."},
{NULL},
};
PyMODINIT_FUNC init_c(void) {
PyObject *module;
module = Py_InitModule3("_c", _c_methods,
"Wrappings of C structures and functions.");
if (pygrpc_add_completion_queue(module) == -1) {
return;
}
if (pygrpc_add_channel(module) == -1) {
return;
}
if (pygrpc_add_call(module) == -1) {
return;
}
if (pygrpc_add_server(module) == -1) {
return;
}
if (pygrpc_add_client_credentials(module) == -1) {
return;
}
if (pygrpc_add_server_credentials(module) == -1) {
return;
}
}

@ -31,21 +31,31 @@
*
*/
#ifndef _ADAPTER__SERVER_H_
#define _ADAPTER__SERVER_H_
#include <stdlib.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_completion_queue.h"
#include "grpc/_adapter/_c/types.h"
typedef struct {
PyObject_HEAD
static PyMethodDef c_methods[] = {
{NULL}
};
CompletionQueue *completion_queue;
grpc_server *c_server;
} Server;
PyMODINIT_FUNC init_c(void) {
PyObject *module;
int pygrpc_add_server(PyObject *module);
module = Py_InitModule3("_c", c_methods,
"Wrappings of C structures and functions.");
#endif /* _ADAPTER__SERVER_H_ */
if (pygrpc_module_add_types(module) < 0) {
return;
}
/* GRPC maintains an internal counter of how many times it has been
initialized and handles multiple pairs of grpc_init()/grpc_shutdown()
invocations accordingly. */
grpc_init();
atexit(&grpc_shutdown);
}

@ -31,19 +31,30 @@
*
*/
#ifndef _ADAPTER__COMPLETION_QUEUE_H_
#define _ADAPTER__COMPLETION_QUEUE_H_
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
typedef struct {
PyObject_HEAD
grpc_completion_queue *c_completion_queue;
} CompletionQueue;
extern PyTypeObject pygrpc_CompletionQueueType;
int pygrpc_add_completion_queue(PyObject *module);
#endif /* _ADAPTER__COMPLETION_QUEUE_H_ */
int pygrpc_module_add_types(PyObject *module) {
int i;
PyTypeObject *types[] = {
&pygrpc_ClientCredentials_type,
&pygrpc_ServerCredentials_type,
&pygrpc_CompletionQueue_type,
&pygrpc_Call_type,
&pygrpc_Channel_type,
&pygrpc_Server_type
};
for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) {
if (PyType_Ready(types[i]) < 0) {
return -1;
}
}
for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) {
Py_INCREF(types[i]);
PyModule_AddObject(module, types[i]->tp_name, (PyObject *)types[i]);
}
return 0;
}

@ -0,0 +1,271 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC__ADAPTER__C_TYPES_H_
#define GRPC__ADAPTER__C_TYPES_H_
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
/*=========================*/
/* Client-side credentials */
/*=========================*/
typedef struct ClientCredentials {
PyObject_HEAD
grpc_credentials *c_creds;
} ClientCredentials;
void pygrpc_ClientCredentials_dealloc(ClientCredentials *self);
ClientCredentials *pygrpc_ClientCredentials_google_default(
PyTypeObject *type, PyObject *ignored);
ClientCredentials *pygrpc_ClientCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ClientCredentials *pygrpc_ClientCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ClientCredentials *pygrpc_ClientCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored);
ClientCredentials *pygrpc_ClientCredentials_service_account(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ClientCredentials *pygrpc_ClientCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ClientCredentials *pygrpc_ClientCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ClientCredentials *pygrpc_ClientCredentials_fake_transport_security(
PyTypeObject *type, PyObject *ignored);
ClientCredentials *pygrpc_ClientCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_ClientCredentials_type;
/*=========================*/
/* Server-side credentials */
/*=========================*/
typedef struct ServerCredentials {
PyObject_HEAD
grpc_server_credentials *c_creds;
} ServerCredentials;
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self);
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
ServerCredentials *pygrpc_ServerCredentials_fake_transport_security(
PyTypeObject *type, PyObject *ignored);
extern PyTypeObject pygrpc_ServerCredentials_type;
/*==================*/
/* Completion queue */
/*==================*/
typedef struct CompletionQueue {
PyObject_HEAD
grpc_completion_queue *c_cq;
} CompletionQueue;
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self);
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored);
extern PyTypeObject pygrpc_CompletionQueue_type;
/*======*/
/* Call */
/*======*/
typedef struct Call {
PyObject_HEAD
grpc_call *c_call;
CompletionQueue *cq;
} Call;
Call *pygrpc_Call_new_empty(CompletionQueue *cq);
void pygrpc_Call_dealloc(Call *self);
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_Call_type;
/*=========*/
/* Channel */
/*=========*/
typedef struct Channel {
PyObject_HEAD
grpc_channel *c_chan;
} Channel;
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Channel_dealloc(Channel *self);
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_Channel_type;
/*========*/
/* Server */
/*========*/
typedef struct Server {
PyObject_HEAD
grpc_server *c_serv;
CompletionQueue *cq;
} Server;
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs);
void pygrpc_Server_dealloc(Server *self);
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs);
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored);
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs);
extern PyTypeObject pygrpc_Server_type;
/*=========*/
/* Utility */
/*=========*/
/* Every tag that passes from Python GRPC to GRPC core is of this type. */
typedef struct pygrpc_tag {
PyObject *user_tag;
Call *call;
grpc_call_details request_call_details;
grpc_metadata_array request_metadata;
grpc_op *ops;
size_t nops;
int is_new_call;
} pygrpc_tag;
/* Construct a tag associated with a batch call. Does not take ownership of the
resources in the elements of ops. */
pygrpc_tag *pygrpc_produce_batch_tag(PyObject *user_tag, Call *call,
grpc_op *ops, size_t nops);
/* Construct a tag associated with a server request. The calling code should
use the appropriate fields of the produced tag in the invocation of
grpc_server_request_call. */
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call);
/* Construct a tag associated with a server shutdown. */
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag);
/* Frees all resources owned by the tag and the tag itself. */
void pygrpc_discard_tag(pygrpc_tag *tag);
/* Consumes an event and its associated tag, providing a Python tuple of the
form `(type, tag, call, call_details, results)` (where type is an integer
corresponding to a grpc_completion_type, tag is an arbitrary PyObject, call
is the call object associated with the event [if any], call_details is a
tuple of form `(method, host, deadline)` [if such details are available],
and resultd is a list of tuples of form `(type, metadata, message, status,
cancelled)` [where type corresponds to a grpc_op_type, metadata is a
sequence of 2-sequences of strings, message is a byte string, and status is
a 2-tuple of an integer corresponding to grpc_status_code and a string of
status details]).
Frees all resources associated with the event tag. */
PyObject *pygrpc_consume_event(grpc_event event);
/* Transliterate the Python tuple of form `(type, metadata, message,
status)` (where type is an integer corresponding to a grpc_op_type, metadata
is a sequence of 2-sequences of strings, message is a byte string, and
status is 2-tuple of an integer corresponding to grpc_status_code and a
string of status details) to a grpc_op suitable for use in a
grpc_call_start_batch invocation. The grpc_op is a 'directory' of resources
that must be freed after GRPC core is done with them.
Calls gpr_malloc (or the appropriate type-specific grpc_*_create function)
to populate the appropriate union-discriminated members of the op.
Returns true on success, false on failure. */
int pygrpc_produce_op(PyObject *op, grpc_op *result);
/* Discards all resources associated with the passed in op that was produced by
pygrpc_produce_op. */
void pygrpc_discard_op(grpc_op op);
/* Transliterate the grpc_ops (which have been sent through a
grpc_call_start_batch invocation and whose corresponding event has appeared
on a completion queue) to a Python tuple of form `(type, metadata, message,
status, cancelled)` (where type is an integer corresponding to a
grpc_op_type, metadata is a sequence of 2-sequences of strings, message is a
byte string, and status is 2-tuple of an integer corresponding to
grpc_status_code and a string of status details).
Calls gpr_free (or the appropriate type-specific grpc_*_destroy function) on
the appropriate union-discriminated populated members of the ops. */
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops);
/* Transliterate from a gpr_timespec to a double (in units of seconds, either
from the epoch if interpreted absolutely or as a delta otherwise). */
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec);
/* Transliterate from a double (in units of seconds from the epoch if
interpreted absolutely or as a delta otherwise) to a gpr_timespec. */
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds);
/* Returns true on success, false on failure. */
int pygrpc_cast_pylist_to_send_metadata(
PyObject *pylist, grpc_metadata **metadata, size_t *count);
/* Returns a metadata array as a Python object on success, else NULL. */
PyObject *pygrpc_cast_metadata_array_to_pylist(grpc_metadata_array metadata);
/* Transliterate from a list of python channel arguments (2-tuples of string
and string|integer|None) to a grpc_channel_args object. The strings placed
in the grpc_channel_args object's grpc_arg elements are views of the Python
object. The Python object must live long enough for the grpc_channel_args
to be used. Arguments set to None are silently ignored. Returns true on
success, false on failure. */
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args);
void pygrpc_discard_channel_args(grpc_channel_args args);
/* Read the bytes from grpc_byte_buffer to a gpr_malloc'd array of bytes;
output to result and result_size. */
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size);
/*========*/
/* Module */
/*========*/
/* Returns 0 on success, -1 on failure. */
int pygrpc_module_add_types(PyObject *module);
#endif /* GRPC__ADAPTER__C_TYPES_H_ */

@ -0,0 +1,163 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_Call_methods[] = {
{"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""},
{"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call.";
PyTypeObject pygrpc_Call_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Call", /* tp_name */
sizeof(Call), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Call_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Call_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Call_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
Call *pygrpc_Call_new_empty(CompletionQueue *cq) {
Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0);
call->c_call = NULL;
call->cq = cq;
Py_XINCREF(call->cq);
return call;
}
void pygrpc_Call_dealloc(Call *self) {
if (self->c_call) {
grpc_call_destroy(self->c_call);
}
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *op_list;
PyObject *user_tag;
grpc_op *ops;
size_t nops;
size_t i;
size_t j;
pygrpc_tag *tag;
grpc_call_error errcode;
static char *keywords[] = {"ops", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords,
&op_list, &user_tag)) {
return NULL;
}
if (!PyList_Check(op_list)) {
PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs");
return NULL;
}
nops = PyList_Size(op_list);
ops = gpr_malloc(sizeof(grpc_op) * nops);
for (i = 0; i < nops; ++i) {
PyObject *item = PyList_GET_ITEM(op_list, i);
if (!pygrpc_produce_op(item, &ops[i])) {
for (j = 0; j < i; ++j) {
pygrpc_discard_op(ops[j]);
}
return NULL;
}
}
tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops);
errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag);
gpr_free(ops);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) {
PyObject *py_code = NULL;
grpc_call_error errcode;
int code;
char *details = NULL;
static char *keywords[] = {"code", "details", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords,
&py_code, &details)) {
return NULL;
}
if (py_code != NULL && details != NULL) {
if (!PyInt_Check(py_code)) {
PyErr_SetString(PyExc_TypeError, "expected integer code");
return NULL;
}
code = PyInt_AsLong(py_code);
errcode = grpc_call_cancel_with_status(self->c_call, code, details);
} else if (py_code != NULL || details != NULL) {
PyErr_SetString(PyExc_ValueError,
"if `code` is specified, so must `details`");
return NULL;
} else {
errcode = grpc_call_cancel(self->c_call);
}
return PyInt_FromLong(errcode);
}

@ -0,0 +1,134 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_Channel_methods[] = {
{"create_call", (PyCFunction)pygrpc_Channel_create_call, METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_Channel_doc[] = "See grpc._adapter._types.Channel.";
PyTypeObject pygrpc_Channel_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Channel", /* tp_name */
sizeof(Channel), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Channel_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Channel_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Channel_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Channel_new /* tp_new */
};
Channel *pygrpc_Channel_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Channel *self;
const char *target;
PyObject *py_args;
ClientCredentials *creds = NULL;
grpc_channel_args c_args;
char *keywords[] = {"target", "args", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!:Channel", keywords,
&target, &py_args, &pygrpc_ClientCredentials_type, &creds)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Channel *)type->tp_alloc(type, 0);
if (creds) {
self->c_chan = grpc_secure_channel_create(creds->c_creds, target, &c_args);
} else {
self->c_chan = grpc_channel_create(target, &c_args);
}
pygrpc_discard_channel_args(c_args);
return self;
}
void pygrpc_Channel_dealloc(Channel *self) {
grpc_channel_destroy(self->c_chan);
self->ob_type->tp_free((PyObject *)self);
}
Call *pygrpc_Channel_create_call(
Channel *self, PyObject *args, PyObject *kwargs) {
Call *call;
CompletionQueue *cq;
const char *method;
const char *host;
double deadline;
char *keywords[] = {"cq", "method", "host", "deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!ssd:create_call", keywords,
&pygrpc_CompletionQueue_type, &cq, &method, &host, &deadline)) {
return NULL;
}
call = pygrpc_Call_new_empty(cq);
call->c_call = grpc_channel_create_call(
self->c_chan, cq->c_cq, method, host,
pygrpc_cast_double_to_gpr_timespec(deadline));
return call;
}

@ -0,0 +1,286 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
PyMethodDef pygrpc_ClientCredentials_methods[] = {
{"google_default", (PyCFunction)pygrpc_ClientCredentials_google_default,
METH_CLASS|METH_NOARGS, ""},
{"ssl", (PyCFunction)pygrpc_ClientCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{"composite", (PyCFunction)pygrpc_ClientCredentials_composite,
METH_CLASS|METH_KEYWORDS, ""},
{"compute_engine", (PyCFunction)pygrpc_ClientCredentials_compute_engine,
METH_CLASS|METH_NOARGS, ""},
{"service_account", (PyCFunction)pygrpc_ClientCredentials_service_account,
METH_CLASS|METH_KEYWORDS, ""},
{"jwt", (PyCFunction)pygrpc_ClientCredentials_jwt,
METH_CLASS|METH_KEYWORDS, ""},
{"refresh_token", (PyCFunction)pygrpc_ClientCredentials_refresh_token,
METH_CLASS|METH_KEYWORDS, ""},
{"fake_transport_security",
(PyCFunction)pygrpc_ClientCredentials_fake_transport_security,
METH_CLASS|METH_NOARGS, ""},
{"iam", (PyCFunction)pygrpc_ClientCredentials_iam,
METH_CLASS|METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_ClientCredentials_doc[] = "";
PyTypeObject pygrpc_ClientCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ClientCredentials", /* tp_name */
sizeof(ClientCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ClientCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ClientCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ClientCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ClientCredentials_dealloc(ClientCredentials *self) {
grpc_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ClientCredentials *pygrpc_ClientCredentials_google_default(
PyTypeObject *type, PyObject *ignored) {
ClientCredentials *self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_google_default_credentials_create();
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create Google default credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
const char *root_certs;
const char *private_key = NULL;
const char *cert_chain = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair;
static char *keywords[] = {"root_certs", "private_key", "cert_chain", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|zz:ssl", keywords,
&root_certs, &private_key, &cert_chain)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
if (private_key && cert_chain) {
key_cert_pair.private_key = private_key;
key_cert_pair.cert_chain = cert_chain;
self->c_creds = grpc_ssl_credentials_create(root_certs, &key_cert_pair);
} else {
self->c_creds = grpc_ssl_credentials_create(root_certs, NULL);
}
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create ssl credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_composite(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
ClientCredentials *creds1;
ClientCredentials *creds2;
static char *keywords[] = {"creds1", "creds2", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords,
&pygrpc_ClientCredentials_type, &creds1,
&pygrpc_ClientCredentials_type, &creds2)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_composite_credentials_create(
creds1->c_creds, creds2->c_creds);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create composite credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_compute_engine(
PyTypeObject *type, PyObject *ignored) {
ClientCredentials *self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_compute_engine_credentials_create();
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create compute engine credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_service_account(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
const char *json_key;
const char *scope;
double lifetime;
static char *keywords[] = {"json_key", "scope", "token_lifetime", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssd:service_account", keywords,
&json_key, &scope, &lifetime)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_service_account_credentials_create(
json_key, scope, pygrpc_cast_double_to_gpr_timespec(lifetime));
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create service account credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_jwt(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
const char *json_key;
double lifetime;
static char *keywords[] = {"json_key", "token_lifetime", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sd:jwt", keywords,
&json_key, &lifetime)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_jwt_credentials_create(
json_key, pygrpc_cast_double_to_gpr_timespec(lifetime));
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create JWT credentials");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_refresh_token(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
const char *json_refresh_token;
static char *keywords[] = {"json_refresh_token", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:refresh_token", keywords,
&json_refresh_token)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_refresh_token_credentials_create(json_refresh_token);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create credentials from refresh token");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_fake_transport_security(
PyTypeObject *type, PyObject *ignored) {
ClientCredentials *self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_fake_transport_security_credentials_create();
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError,
"couldn't create fake credentials; "
"something is horribly wrong with the universe");
return NULL;
}
return self;
}
ClientCredentials *pygrpc_ClientCredentials_iam(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ClientCredentials *self;
const char *authorization_token;
const char *authority_selector;
static char *keywords[] = {"authorization_token", "authority_selector", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss:iam", keywords,
&authorization_token, &authority_selector)) {
return NULL;
}
self = (ClientCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_iam_credentials_create(authorization_token,
authority_selector);
if (!self->c_creds) {
Py_DECREF(self);
PyErr_SetString(PyExc_RuntimeError, "couldn't create IAM credentials");
return NULL;
}
return self;
}

@ -0,0 +1,124 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_CompletionQueue_methods[] = {
{"next", (PyCFunction)pygrpc_CompletionQueue_next, METH_KEYWORDS, ""},
{"shutdown", (PyCFunction)pygrpc_CompletionQueue_shutdown, METH_NOARGS, ""},
{NULL}
};
const char pygrpc_CompletionQueue_doc[] =
"See grpc._adapter._types.CompletionQueue.";
PyTypeObject pygrpc_CompletionQueue_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CompletionQueue", /* tp_name */
sizeof(CompletionQueue), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_CompletionQueue_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_CompletionQueue_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_CompletionQueue_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_CompletionQueue_new /* tp_new */
};
CompletionQueue *pygrpc_CompletionQueue_new(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
CompletionQueue *self = (CompletionQueue *)type->tp_alloc(type, 0);
self->c_cq = grpc_completion_queue_create();
return self;
}
void pygrpc_CompletionQueue_dealloc(CompletionQueue *self) {
grpc_completion_queue_destroy(self->c_cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_CompletionQueue_next(
CompletionQueue *self, PyObject *args, PyObject *kwargs) {
double deadline;
grpc_event event;
PyObject *transliterated_event;
static char *keywords[] = {"deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:next", keywords,
&deadline)) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
event = grpc_completion_queue_next(
self->c_cq, pygrpc_cast_double_to_gpr_timespec(deadline));
Py_END_ALLOW_THREADS;
transliterated_event = pygrpc_consume_event(event);
return transliterated_event;
}
PyObject *pygrpc_CompletionQueue_shutdown(
CompletionQueue *self, PyObject *ignored) {
grpc_completion_queue_shutdown(self->c_cq);
Py_RETURN_NONE;
}

@ -0,0 +1,183 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
PyMethodDef pygrpc_Server_methods[] = {
{"request_call", (PyCFunction)pygrpc_Server_request_call,
METH_KEYWORDS, ""},
{"add_http2_port", (PyCFunction)pygrpc_Server_add_http2_port,
METH_KEYWORDS, ""},
{"start", (PyCFunction)pygrpc_Server_start, METH_NOARGS, ""},
{"shutdown", (PyCFunction)pygrpc_Server_shutdown, METH_KEYWORDS, ""},
{NULL}
};
const char pygrpc_Server_doc[] = "See grpc._adapter._types.Server.";
PyTypeObject pygrpc_Server_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Server", /* tp_name */
sizeof(Server), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_Server_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_Server_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_Server_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
(newfunc)pygrpc_Server_new /* tp_new */
};
Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
Server *self;
CompletionQueue *cq;
PyObject *py_args;
grpc_channel_args c_args;
char *keywords[] = {"cq", "args", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Channel", keywords,
&pygrpc_CompletionQueue_type, &cq, &py_args)) {
return NULL;
}
if (!pygrpc_produce_channel_args(py_args, &c_args)) {
return NULL;
}
self = (Server *)type->tp_alloc(type, 0);
self->c_serv = grpc_server_create(&c_args);
pygrpc_discard_channel_args(c_args);
self->cq = cq;
Py_INCREF(self->cq);
return self;
}
void pygrpc_Server_dealloc(Server *self) {
grpc_server_destroy(self->c_serv);
Py_XDECREF(self->cq);
self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Server_request_call(
Server *self, PyObject *args, PyObject *kwargs) {
CompletionQueue *cq;
PyObject *user_tag;
pygrpc_tag *tag;
Call *empty_call;
grpc_call_error errcode;
static char *keywords[] = {"cq", "tag", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "O!O", keywords,
&pygrpc_CompletionQueue_type, &cq, &user_tag)) {
return NULL;
}
empty_call = pygrpc_Call_new_empty(cq);
tag = pygrpc_produce_request_tag(user_tag, empty_call);
errcode = grpc_server_request_call(
self->c_serv, &tag->call->c_call, &tag->request_call_details,
&tag->request_metadata, tag->call->cq->c_cq, self->cq->c_cq, tag);
Py_DECREF(empty_call);
return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Server_add_http2_port(
Server *self, PyObject *args, PyObject *kwargs) {
const char *addr;
ServerCredentials *creds = NULL;
int port;
static char *keywords[] = {"addr", "creds", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "s|O!:add_http2_port", keywords,
&addr, &pygrpc_ServerCredentials_type, &creds)) {
return NULL;
}
if (creds) {
port = grpc_server_add_secure_http2_port(
self->c_serv, addr, creds->c_creds);
} else {
port = grpc_server_add_http2_port(self->c_serv, addr);
}
return PyInt_FromLong(port);
}
PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) {
grpc_server_start(self->c_serv);
Py_RETURN_NONE;
}
PyObject *pygrpc_Server_shutdown(
Server *self, PyObject *args, PyObject *kwargs) {
PyObject *user_tag = NULL;
pygrpc_tag *tag;
static char *keywords[] = {"tag", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", keywords, &user_tag)) {
return NULL;
}
if (user_tag) {
tag = pygrpc_produce_server_shutdown_tag(user_tag);
grpc_server_shutdown_and_notify(self->c_serv, tag);
} else {
grpc_server_shutdown(self->c_serv);
}
Py_RETURN_NONE;
}

@ -0,0 +1,146 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_c/types.h"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
PyMethodDef pygrpc_ServerCredentials_methods[] = {
{"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl,
METH_CLASS|METH_KEYWORDS, ""},
{"fake_transport_security",
(PyCFunction)pygrpc_ServerCredentials_fake_transport_security,
METH_CLASS|METH_NOARGS, ""},
{NULL}
};
const char pygrpc_ServerCredentials_doc[] = "";
PyTypeObject pygrpc_ServerCredentials_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ServerCredentials", /* tp_name */
sizeof(ServerCredentials), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)pygrpc_ServerCredentials_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
pygrpc_ServerCredentials_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
pygrpc_ServerCredentials_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
void pygrpc_ServerCredentials_dealloc(ServerCredentials *self) {
grpc_server_credentials_release(self->c_creds);
self->ob_type->tp_free((PyObject *)self);
}
ServerCredentials *pygrpc_ServerCredentials_ssl(
PyTypeObject *type, PyObject *args, PyObject *kwargs) {
ServerCredentials *self;
const char *root_certs;
PyObject *py_key_cert_pairs;
grpc_ssl_pem_key_cert_pair *key_cert_pairs;
size_t num_key_cert_pairs;
size_t i;
static char *keywords[] = {"root_certs", "key_cert_pairs", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zO:ssl", keywords,
&root_certs, &py_key_cert_pairs)) {
return NULL;
}
if (!PyList_Check(py_key_cert_pairs)) {
PyErr_SetString(PyExc_TypeError, "expected a list of 2-tuples of strings");
return NULL;
}
num_key_cert_pairs = PyList_Size(py_key_cert_pairs);
key_cert_pairs =
gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs);
for (i = 0; i < num_key_cert_pairs; ++i) {
PyObject *item = PyList_GET_ITEM(py_key_cert_pairs, i);
const char *key;
const char *cert;
if (!PyArg_ParseTuple(item, "zz", &key, &cert)) {
gpr_free(key_cert_pairs);
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuples of strings");
return NULL;
}
key_cert_pairs[i].private_key = key;
key_cert_pairs[i].cert_chain = cert;
}
self = (ServerCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_ssl_server_credentials_create(
root_certs, key_cert_pairs, num_key_cert_pairs);
gpr_free(key_cert_pairs);
return self;
}
ServerCredentials *pygrpc_ServerCredentials_fake_transport_security(
PyTypeObject *type, PyObject *ignored) {
ServerCredentials *self = (ServerCredentials *)type->tp_alloc(type, 0);
self->c_creds = grpc_fake_transport_security_server_credentials_create();
return self;
}

@ -0,0 +1,460 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <math.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/slice.h>
#include <grpc/support/time.h>
#include "grpc/_adapter/_c/types.h"
pygrpc_tag *pygrpc_produce_batch_tag(
PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = call;
Py_XINCREF(tag->call);
tag->ops = gpr_malloc(sizeof(grpc_op)*nops);
memcpy(tag->ops, ops, sizeof(grpc_op)*nops);
tag->nops = nops;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = empty_call;
Py_XINCREF(tag->call);
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 1;
return tag;
}
pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) {
pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag));
tag->user_tag = user_tag;
Py_XINCREF(tag->user_tag);
tag->call = NULL;
tag->ops = NULL;
tag->nops = 0;
grpc_call_details_init(&tag->request_call_details);
grpc_metadata_array_init(&tag->request_metadata);
tag->is_new_call = 0;
return tag;
}
void pygrpc_discard_tag(pygrpc_tag *tag) {
if (!tag) {
return;
}
Py_XDECREF(tag->user_tag);
Py_XDECREF(tag->call);
gpr_free(tag->ops);
grpc_call_details_destroy(&tag->request_call_details);
grpc_metadata_array_destroy(&tag->request_metadata);
gpr_free(tag);
}
PyObject *pygrpc_consume_event(grpc_event event) {
pygrpc_tag *tag;
PyObject *result;
if (event.type == GRPC_QUEUE_TIMEOUT) {
Py_RETURN_NONE;
}
tag = event.tag;
switch (event.type) {
case GRPC_QUEUE_SHUTDOWN:
result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN,
Py_None, Py_None, Py_None, Py_None, Py_True);
break;
case GRPC_OP_COMPLETE:
if (tag->is_new_call) {
result = Py_BuildValue(
"iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call,
tag->request_call_details.method, tag->request_call_details.host,
pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline),
GRPC_OP_RECV_INITIAL_METADATA,
pygrpc_cast_metadata_array_to_pylist(tag->request_metadata), Py_None,
Py_None, Py_None, Py_None,
event.success ? Py_True : Py_False);
} else {
result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag,
tag->call, Py_None, pygrpc_consume_ops(tag->ops, tag->nops),
event.success ? Py_True : Py_False);
}
break;
default:
PyErr_SetString(PyExc_ValueError,
"unknown completion type; could not translate event");
return NULL;
}
pygrpc_discard_tag(tag);
return result;
}
int pygrpc_produce_op(PyObject *op, grpc_op *result) {
static const int OP_TUPLE_SIZE = 5;
static const int STATUS_TUPLE_SIZE = 2;
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int STATUS_CODE_INDEX = 0;
static const int STATUS_DETAILS_INDEX = 1;
grpc_op c_op;
if (!PyTuple_Check(op)) {
PyErr_SetString(PyExc_TypeError, "expected tuple op");
return 0;
}
if (PyTuple_Size(op) != OP_TUPLE_SIZE) {
char buf[64];
snprintf(buf, sizeof(buf), "expected tuple op of length %d", OP_TUPLE_SIZE);
PyErr_SetString(PyExc_ValueError, buf);
return 0;
}
int type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX));
if (PyErr_Occurred()) {
return 0;
}
Py_ssize_t message_size;
char *message;
char *status_details;
gpr_slice message_slice;
c_op.op = type;
switch (type) {
case GRPC_OP_SEND_INITIAL_METADATA:
if (!pygrpc_cast_pylist_to_send_metadata(
PyTuple_GetItem(op, INITIAL_METADATA_INDEX),
&c_op.data.send_initial_metadata.metadata,
&c_op.data.send_initial_metadata.count)) {
return 0;
}
break;
case GRPC_OP_SEND_MESSAGE:
PyString_AsStringAndSize(
PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size);
message_slice = gpr_slice_from_copied_buffer(message, message_size);
c_op.data.send_message = grpc_byte_buffer_create(&message_slice, 1);
gpr_slice_unref(message_slice);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to fill in any other fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
if (!pygrpc_cast_pylist_to_send_metadata(
PyTuple_GetItem(op, TRAILING_METADATA_INDEX),
&c_op.data.send_status_from_server.trailing_metadata,
&c_op.data.send_status_from_server.trailing_metadata_count)) {
return 0;
}
if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) {
char buf[64];
snprintf(buf, sizeof(buf), "expected tuple status in op of length %d",
STATUS_TUPLE_SIZE);
PyErr_SetString(PyExc_TypeError, buf);
return 0;
}
c_op.data.send_status_from_server.status = PyInt_AsLong(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX));
status_details = PyString_AsString(
PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX));
if (PyErr_Occurred()) {
return 0;
}
c_op.data.send_status_from_server.status_details =
gpr_malloc(strlen(status_details) + 1);
strcpy((char *)c_op.data.send_status_from_server.status_details,
status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *));
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
c_op.data.recv_status_on_client.trailing_metadata =
gpr_malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata);
c_op.data.recv_status_on_client.status =
gpr_malloc(sizeof(grpc_status_code *));
c_op.data.recv_status_on_client.status_details =
gpr_malloc(sizeof(char *));
*c_op.data.recv_status_on_client.status_details = NULL;
c_op.data.recv_status_on_client.status_details_capacity =
gpr_malloc(sizeof(size_t));
*c_op.data.recv_status_on_client.status_details_capacity = 0;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int));
break;
default:
return 0;
}
*result = c_op;
return 1;
}
void pygrpc_discard_op(grpc_op op) {
switch(op.op) {
case GRPC_OP_SEND_INITIAL_METADATA:
gpr_free(op.data.send_initial_metadata.metadata);
break;
case GRPC_OP_SEND_MESSAGE:
grpc_byte_buffer_destroy(op.data.send_message);
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Don't need to free any fields. */
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
gpr_free(op.data.send_status_from_server.trailing_metadata);
gpr_free((char *)op.data.send_status_from_server.status_details);
break;
case GRPC_OP_RECV_INITIAL_METADATA:
grpc_metadata_array_destroy(op.data.recv_initial_metadata);
gpr_free(op.data.recv_initial_metadata);
break;
case GRPC_OP_RECV_MESSAGE:
grpc_byte_buffer_destroy(*op.data.recv_message);
gpr_free(op.data.recv_message);
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.trailing_metadata);
gpr_free(op.data.recv_status_on_client.status);
gpr_free(*op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details);
gpr_free(op.data.recv_status_on_client.status_details_capacity);
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
gpr_free(op.data.recv_close_on_server.cancelled);
break;
}
}
PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) {
static const int TYPE_INDEX = 0;
static const int INITIAL_METADATA_INDEX = 1;
static const int TRAILING_METADATA_INDEX = 2;
static const int MESSAGE_INDEX = 3;
static const int STATUS_INDEX = 4;
static const int CANCELLED_INDEX = 5;
static const int OPRESULT_LENGTH = 6;
PyObject *list;
size_t i;
size_t j;
char *bytes;
size_t bytes_size;
PyObject *results = PyList_New(nops);
if (!results) {
return NULL;
}
for (i = 0; i < nops; ++i) {
PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None,
Py_None, Py_None, Py_None);
PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op));
switch(op[i].op) {
case GRPC_OP_RECV_INITIAL_METADATA:
PyTuple_SetItem(result, INITIAL_METADATA_INDEX,
list=PyList_New(op[i].data.recv_initial_metadata->count));
for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) {
grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
break;
case GRPC_OP_RECV_MESSAGE:
if (*op[i].data.recv_message) {
pygrpc_byte_buffer_to_bytes(
*op[i].data.recv_message, &bytes, &bytes_size);
PyTuple_SetItem(result, MESSAGE_INDEX,
PyString_FromStringAndSize(bytes, bytes_size));
gpr_free(bytes);
} else {
PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue(""));
}
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
PyTuple_SetItem(
result, TRAILING_METADATA_INDEX,
list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count));
for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) {
grpc_metadata md =
op[i].data.recv_status_on_client.trailing_metadata->metadata[j];
PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value,
(Py_ssize_t)md.value_length));
}
PyTuple_SetItem(
result, STATUS_INDEX, Py_BuildValue(
"is", *op[i].data.recv_status_on_client.status,
*op[i].data.recv_status_on_client.status_details));
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
PyTuple_SetItem(
result, CANCELLED_INDEX,
PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled));
break;
default:
break;
}
pygrpc_discard_op(op[i]);
PyList_SetItem(results, i, result);
}
return results;
}
double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) {
return timespec.tv_sec + 1e-9*timespec.tv_nsec;
}
gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) {
gpr_timespec result;
if isinf(seconds) {
result = seconds > 0.0 ? gpr_inf_future : gpr_inf_past;
} else {
result.tv_sec = (time_t)seconds;
result.tv_nsec = ((seconds - result.tv_sec) * 1e9);
}
return result;
}
int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) {
size_t num_args = PyList_Size(py_args);
size_t i;
grpc_channel_args args = {num_args, gpr_malloc(sizeof(grpc_arg) * num_args)};
for (i = 0; i < args.num_args; ++i) {
char *key;
PyObject *value;
if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
args.args[i].key = key;
if (PyInt_Check(value)) {
args.args[i].type = GRPC_ARG_INTEGER;
args.args[i].value.integer = PyInt_AsLong(value);
} else if (PyString_Check(value)) {
args.args[i].type = GRPC_ARG_STRING;
args.args[i].value.string = PyString_AsString(value);
} else if (value == Py_None) {
--args.num_args;
--i;
continue;
} else {
gpr_free(args.args);
args.num_args = 0;
args.args = NULL;
PyErr_SetString(PyExc_TypeError,
"expected a list of 2-tuple of str and str|int|None");
return 0;
}
}
*c_args = args;
return 1;
}
void pygrpc_discard_channel_args(grpc_channel_args args) {
gpr_free(args.args);
}
int pygrpc_cast_pylist_to_send_metadata(
PyObject *pylist, grpc_metadata **metadata, size_t *count) {
size_t i;
Py_ssize_t value_length;
*count = PyList_Size(pylist);
*metadata = gpr_malloc(sizeof(grpc_metadata) * *count);
for (i = 0; i < *count; ++i) {
if (!PyArg_ParseTuple(
PyList_GetItem(pylist, i), "ss#",
&(*metadata)[i].key, &(*metadata)[i].value, &value_length)) {
gpr_free(*metadata);
*count = 0;
*metadata = NULL;
return 0;
}
(*metadata)[i].value_length = value_length;
}
return 1;
}
PyObject *pygrpc_cast_metadata_array_to_pylist(grpc_metadata_array metadata) {
PyObject *result = PyList_New(metadata.count);
size_t i;
for (i = 0; i < metadata.count; ++i) {
PyList_SetItem(
result, i, Py_BuildValue(
"ss#", metadata.metadata[i].key, metadata.metadata[i].value,
(Py_ssize_t)metadata.metadata[i].value_length));
if (PyErr_Occurred()) {
Py_DECREF(result);
return NULL;
}
}
return result;
}
void pygrpc_byte_buffer_to_bytes(
grpc_byte_buffer *buffer, char **result, size_t *result_size) {
grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer);
gpr_slice slice;
char *read_result = NULL;
size_t size = 0;
while (grpc_byte_buffer_reader_next(reader, &slice)) {
read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice));
memcpy(read_result + size, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
size = size + GPR_SLICE_LENGTH(slice);
gpr_slice_unref(slice);
}
grpc_byte_buffer_reader_destroy(reader);
*result_size = size;
*result = read_result;
}

@ -27,192 +27,40 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tests for _adapter._c."""
import threading
import time
import unittest
from grpc._adapter import _c
from grpc._adapter import _datatypes
_TIMEOUT = 3
_FUTURE = time.time() + 60 * 60 * 24
_IDEMPOTENCE_DEMONSTRATION = 7
class _CTest(unittest.TestCase):
def testUpAndDown(self):
_c.init()
_c.shut_down()
def testCompletionQueue(self):
_c.init()
completion_queue = _c.CompletionQueue()
event = completion_queue.get(0)
self.assertIsNone(event)
event = completion_queue.get(time.time())
self.assertIsNone(event)
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIsNone(event)
completion_queue.stop()
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
del completion_queue
del event
from grpc._adapter import _types
_c.shut_down()
def testChannel(self):
_c.init()
channel = _c.Channel(
'test host:12345', None, server_host_override='ignored')
del channel
_c.shut_down()
def testCall(self):
method = 'test method'
host = 'test host'
_c.init()
channel = _c.Channel('%s:%d' % (host, 12345), None)
completion_queue = _c.CompletionQueue()
call = _c.Call(channel, completion_queue, method, host,
time.time() + _TIMEOUT)
del call
del completion_queue
del channel
class CTypeSmokeTest(unittest.TestCase):
_c.shut_down()
def testClientCredentialsUpDown(self):
credentials = _c.ClientCredentials.fake_transport_security()
del credentials
def testServer(self):
_c.init()
def testServerCredentialsUpDown(self):
credentials = _c.ServerCredentials.fake_transport_security()
del credentials
def testCompletionQueueUpDown(self):
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
server.stop()
completion_queue.stop()
del server
del completion_queue
service_tag = object()
def testServerUpDown(self):
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
server.service(service_tag)
server.stop()
completion_queue.stop()
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.SERVICE_ACCEPTED)
self.assertIs(event.tag, service_tag)
self.assertIsNone(event.service_acceptance)
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
del server
serv = _c.Server(completion_queue, [])
del serv
del completion_queue
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue)
server.add_http2_addr('[::]:0')
server.start()
thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,))
thread.start()
time.sleep(1)
server.stop()
completion_queue.stop()
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
thread.join()
del server
del completion_queue
_c.shut_down()
def test_client_credentials(self):
root_certificates = b'Trust starts here. Really.'
private_key = b'This is a really bad private key, yo.'
certificate_chain = b'Trust me! Do I not look trustworty?'
_c.init()
client_credentials = _c.ClientCredentials(
None, None, None)
self.assertIsNotNone(client_credentials)
client_credentials = _c.ClientCredentials(
root_certificates, None, None)
self.assertIsNotNone(client_credentials)
client_credentials = _c.ClientCredentials(
None, private_key, certificate_chain)
self.assertIsNotNone(client_credentials)
client_credentials = _c.ClientCredentials(
root_certificates, private_key, certificate_chain)
self.assertIsNotNone(client_credentials)
del client_credentials
_c.shut_down()
def test_server_credentials(self):
root_certificates = b'Trust starts here. Really.'
first_private_key = b'This is a really bad private key, yo.'
first_certificate_chain = b'Trust me! Do I not look trustworty?'
second_private_key = b'This is another bad private key, yo.'
second_certificate_chain = b'Look into my eyes; you can totes trust me.'
_c.init()
server_credentials = _c.ServerCredentials(
None, ((first_private_key, first_certificate_chain),))
del server_credentials
server_credentials = _c.ServerCredentials(
root_certificates, ((first_private_key, first_certificate_chain),))
del server_credentials
server_credentials = _c.ServerCredentials(
root_certificates,
((first_private_key, first_certificate_chain),
(second_private_key, second_certificate_chain),))
del server_credentials
with self.assertRaises(TypeError):
_c.ServerCredentials(
root_certificates, first_private_key, second_certificate_chain)
_c.shut_down()
@unittest.skip('TODO(nathaniel): find and use real-enough test credentials')
def test_secure_server(self):
_c.init()
server_credentials = _c.ServerCredentials(
'root certificate', (('private key', 'certificate chain'),))
completion_queue = _c.CompletionQueue()
server = _c.Server(completion_queue, server_credentials)
server.add_http2_addr('[::]:0')
server.start()
thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,))
thread.start()
time.sleep(1)
server.stop()
completion_queue.stop()
for _ in range(_IDEMPOTENCE_DEMONSTRATION):
event = completion_queue.get(time.time() + _TIMEOUT)
self.assertIs(event.kind, _datatypes.Event.Kind.STOP)
thread.join()
del server
del completion_queue
def testChannelUpDown(self):
channel = _c.Channel('[::]:0', [])
del channel
_c.shut_down()
def testSecureChannelUpDown(self):
channel = _c.Channel('[::]:0', [], _c.ClientCredentials.fake_transport_security())
del channel
if __name__ == '__main__':

@ -1,438 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_call.h"
#include <math.h>
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include "grpc/_adapter/_channel.h"
#include "grpc/_adapter/_completion_queue.h"
#include "grpc/_adapter/_error.h"
#include "grpc/_adapter/_tag.h"
static PyObject *pygrpc_call_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
Call *self = (Call *)type->tp_alloc(type, 0);
Channel *channel;
CompletionQueue *completion_queue;
const char *method;
const char *host;
double deadline;
static char *kwlist[] = {"channel", "completion_queue",
"method", "host", "deadline", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kwds, "O!O!ssd:Call", kwlist,
&pygrpc_ChannelType, &channel,
&pygrpc_CompletionQueueType, &completion_queue,
&method, &host, &deadline)) {
return NULL;
}
/* TODO(nathaniel): Hoist the gpr_timespec <-> PyFloat arithmetic into its own
* function with its own test coverage.
*/
self->c_call = grpc_channel_create_call(
channel->c_channel, completion_queue->c_completion_queue, method, host,
gpr_time_from_nanos(deadline * GPR_NS_PER_SEC));
self->completion_queue = completion_queue;
Py_INCREF(self->completion_queue);
self->channel = channel;
Py_INCREF(self->channel);
grpc_call_details_init(&self->call_details);
grpc_metadata_array_init(&self->recv_metadata);
grpc_metadata_array_init(&self->recv_trailing_metadata);
self->send_metadata = NULL;
self->send_metadata_count = 0;
self->send_trailing_metadata = NULL;
self->send_trailing_metadata_count = 0;
self->send_message = NULL;
self->recv_message = NULL;
self->adding_to_trailing = 0;
return (PyObject *)self;
}
static void pygrpc_call_dealloc(Call *self) {
if (self->c_call != NULL) {
grpc_call_destroy(self->c_call);
}
Py_XDECREF(self->completion_queue);
Py_XDECREF(self->channel);
Py_XDECREF(self->server);
grpc_call_details_destroy(&self->call_details);
grpc_metadata_array_destroy(&self->recv_metadata);
grpc_metadata_array_destroy(&self->recv_trailing_metadata);
if (self->send_message) {
grpc_byte_buffer_destroy(self->send_message);
}
if (self->recv_message) {
grpc_byte_buffer_destroy(self->recv_message);
}
gpr_free(self->status_details);
gpr_free(self->send_metadata);
gpr_free(self->send_trailing_metadata);
self->ob_type->tp_free((PyObject *)self);
}
static const PyObject *pygrpc_call_invoke(Call *self, PyObject *args) {
PyObject *completion_queue;
PyObject *metadata_tag;
PyObject *finish_tag;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_init_metadata_tag;
pygrpc_tag *c_metadata_tag;
pygrpc_tag *c_finish_tag;
grpc_op send_initial_metadata;
grpc_op recv_initial_metadata;
grpc_op recv_status_on_client;
if (!(PyArg_ParseTuple(args, "O!OO:invoke", &pygrpc_CompletionQueueType,
&completion_queue, &metadata_tag, &finish_tag))) {
return NULL;
}
send_initial_metadata.op = GRPC_OP_SEND_INITIAL_METADATA;
send_initial_metadata.data.send_initial_metadata.metadata = self->send_metadata;
send_initial_metadata.data.send_initial_metadata.count = self->send_metadata_count;
recv_initial_metadata.op = GRPC_OP_RECV_INITIAL_METADATA;
recv_initial_metadata.data.recv_initial_metadata = &self->recv_metadata;
recv_status_on_client.op = GRPC_OP_RECV_STATUS_ON_CLIENT;
recv_status_on_client.data.recv_status_on_client.trailing_metadata = &self->recv_trailing_metadata;
recv_status_on_client.data.recv_status_on_client.status = &self->status;
recv_status_on_client.data.recv_status_on_client.status_details = &self->status_details;
recv_status_on_client.data.recv_status_on_client.status_details_capacity = &self->status_details_capacity;
c_init_metadata_tag = pygrpc_tag_new(PYGRPC_INITIAL_METADATA, NULL, self);
c_metadata_tag = pygrpc_tag_new(PYGRPC_CLIENT_METADATA_READ, metadata_tag, self);
c_finish_tag = pygrpc_tag_new(PYGRPC_FINISHED_CLIENT, finish_tag, self);
call_error = grpc_call_start_batch(self->c_call, &send_initial_metadata, 1, c_init_metadata_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_init_metadata_tag);
pygrpc_tag_destroy(c_metadata_tag);
pygrpc_tag_destroy(c_finish_tag);
return result;
}
call_error = grpc_call_start_batch(self->c_call, &recv_initial_metadata, 1, c_metadata_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_metadata_tag);
pygrpc_tag_destroy(c_finish_tag);
return result;
}
call_error = grpc_call_start_batch(self->c_call, &recv_status_on_client, 1, c_finish_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_finish_tag);
return result;
}
return result;
}
static const PyObject *pygrpc_call_write(Call *self, PyObject *args) {
const char *bytes;
int length;
PyObject *tag;
gpr_slice slice;
grpc_byte_buffer *byte_buffer;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag;
grpc_op op;
if (!(PyArg_ParseTuple(args, "s#O:write", &bytes, &length, &tag))) {
return NULL;
}
c_tag = pygrpc_tag_new(PYGRPC_WRITE_ACCEPTED, tag, self);
slice = gpr_slice_from_copied_buffer(bytes, length);
byte_buffer = grpc_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
if (self->send_message) {
grpc_byte_buffer_destroy(self->send_message);
}
self->send_message = byte_buffer;
op.op = GRPC_OP_SEND_MESSAGE;
op.data.send_message = self->send_message;
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_complete(Call *self, PyObject *tag) {
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag = pygrpc_tag_new(PYGRPC_FINISH_ACCEPTED, tag, self);
grpc_op op;
op.op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_accept(Call *self, PyObject *args) {
PyObject *completion_queue;
PyObject *tag;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag;
grpc_op op;
if (!(PyArg_ParseTuple(args, "O!O:accept", &pygrpc_CompletionQueueType,
&completion_queue, &tag))) {
return NULL;
}
op.op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op.data.recv_close_on_server.cancelled = &self->cancelled;
c_tag = pygrpc_tag_new(PYGRPC_FINISHED_SERVER, tag, self);
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_add_metadata(Call *self, PyObject *args) {
const char* key = NULL;
const char* value = NULL;
int value_length = 0;
grpc_metadata metadata;
if (!PyArg_ParseTuple(args, "ss#", &key, &value, &value_length)) {
return NULL;
}
metadata.key = key;
metadata.value = value;
metadata.value_length = value_length;
if (self->adding_to_trailing) {
self->send_trailing_metadata = gpr_realloc(self->send_trailing_metadata, (self->send_trailing_metadata_count + 1) * sizeof(grpc_metadata));
self->send_trailing_metadata[self->send_trailing_metadata_count] = metadata;
self->send_trailing_metadata_count = self->send_trailing_metadata_count + 1;
} else {
self->send_metadata = gpr_realloc(self->send_metadata, (self->send_metadata_count + 1) * sizeof(grpc_metadata));
self->send_metadata[self->send_metadata_count] = metadata;
self->send_metadata_count = self->send_metadata_count + 1;
}
return pygrpc_translate_call_error(GRPC_CALL_OK);
}
static const PyObject *pygrpc_call_premetadata(Call *self) {
grpc_op op;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag = pygrpc_tag_new(PYGRPC_INITIAL_METADATA, NULL, self);
op.op = GRPC_OP_SEND_INITIAL_METADATA;
op.data.send_initial_metadata.metadata = self->send_metadata;
op.data.send_initial_metadata.count = self->send_metadata_count;
self->adding_to_trailing = 1;
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_read(Call *self, PyObject *tag) {
grpc_op op;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag = pygrpc_tag_new(PYGRPC_READ, tag, self);
op.op = GRPC_OP_RECV_MESSAGE;
if (self->recv_message) {
grpc_byte_buffer_destroy(self->recv_message);
self->recv_message = NULL;
}
op.data.recv_message = &self->recv_message;
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_status(Call *self, PyObject *args) {
PyObject *status;
PyObject *code;
PyObject *details;
PyObject *tag;
grpc_status_code c_code;
char *c_message;
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag;
grpc_op op;
if (!(PyArg_ParseTuple(args, "OO:status", &status, &tag))) {
return NULL;
}
c_tag = pygrpc_tag_new(PYGRPC_FINISH_ACCEPTED, tag, self);
code = PyObject_GetAttrString(status, "code");
if (code == NULL) {
return NULL;
}
details = PyObject_GetAttrString(status, "details");
if (details == NULL) {
Py_DECREF(code);
return NULL;
}
c_code = PyInt_AsLong(code);
Py_DECREF(code);
if (c_code == -1 && PyErr_Occurred()) {
Py_DECREF(details);
return NULL;
}
c_message = PyBytes_AsString(details);
Py_DECREF(details);
if (c_message == NULL) {
return NULL;
}
if (self->status_details) {
gpr_free(self->status_details);
}
self->status_details = gpr_malloc(strlen(c_message)+1);
strcpy(self->status_details, c_message);
op.op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op.data.send_status_from_server.trailing_metadata_count = self->send_trailing_metadata_count;
op.data.send_status_from_server.trailing_metadata = self->send_trailing_metadata;
op.data.send_status_from_server.status = c_code;
op.data.send_status_from_server.status_details = self->status_details;
call_error = grpc_call_start_batch(self->c_call, &op, 1, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result == NULL) {
pygrpc_tag_destroy(c_tag);
}
return result;
}
static const PyObject *pygrpc_call_cancel(Call *self) {
return pygrpc_translate_call_error(grpc_call_cancel(self->c_call));
}
static PyMethodDef methods[] = {
{"invoke", (PyCFunction)pygrpc_call_invoke, METH_VARARGS,
"Invoke this call."},
{"write", (PyCFunction)pygrpc_call_write, METH_VARARGS,
"Write bytes to this call."},
{"complete", (PyCFunction)pygrpc_call_complete, METH_O,
"Complete writes to this call."},
{"accept", (PyCFunction)pygrpc_call_accept, METH_VARARGS, "Accept an RPC."},
{"add_metadata", (PyCFunction)pygrpc_call_add_metadata, METH_VARARGS,
"Add metadata to the call. May not be called after invoke on the client "
"side. On the server side: when called before premetadata it provides "
"'leading' metadata, when called after premetadata but before status it "
"provides 'trailing metadata'; may not be called after status."},
{"premetadata", (PyCFunction)pygrpc_call_premetadata, METH_VARARGS,
"Indicate the end of leading metadata in the response."},
{"read", (PyCFunction)pygrpc_call_read, METH_O,
"Read bytes from this call."},
{"status", (PyCFunction)pygrpc_call_status, METH_VARARGS,
"Report this call's status."},
{"cancel", (PyCFunction)pygrpc_call_cancel, METH_NOARGS,
"Cancel this call."},
{NULL}};
PyTypeObject pygrpc_CallType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_grpc.Call", /*tp_name*/
sizeof(Call), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_call_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_call.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
pygrpc_call_new, /* tp_new */
};
int pygrpc_add_call(PyObject *module) {
if (PyType_Ready(&pygrpc_CallType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "Call", (PyObject *)&pygrpc_CallType) == -1) {
return -1;
}
return 0;
}

@ -1,77 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__CALL_H_
#define _ADAPTER__CALL_H_
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_completion_queue.h"
#include "grpc/_adapter/_channel.h"
#include "grpc/_adapter/_server.h"
typedef struct {
PyObject_HEAD
CompletionQueue *completion_queue;
Channel *channel;
Server *server;
/* Legacy state. */
grpc_call_details call_details;
grpc_metadata_array recv_metadata;
grpc_metadata_array recv_trailing_metadata;
grpc_metadata *send_metadata;
size_t send_metadata_count;
grpc_metadata *send_trailing_metadata;
size_t send_trailing_metadata_count;
int adding_to_trailing;
grpc_byte_buffer *send_message;
grpc_byte_buffer *recv_message;
grpc_status_code status;
char *status_details;
size_t status_details_capacity;
int cancelled;
grpc_call *c_call;
} Call;
extern PyTypeObject pygrpc_CallType;
int pygrpc_add_call(PyObject *module);
#endif /* _ADAPTER__CALL_H_ */

@ -1,135 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_channel.h"
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include "grpc/_adapter/_client_credentials.h"
static int pygrpc_channel_init(Channel *self, PyObject *args, PyObject *kwds) {
const char *hostport;
PyObject *client_credentials;
char *server_host_override = NULL;
static char *kwlist[] = {"hostport", "client_credentials",
"server_host_override", NULL};
grpc_arg server_host_override_arg;
grpc_channel_args channel_args;
if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO|z:Channel", kwlist,
&hostport, &client_credentials,
&server_host_override))) {
return -1;
}
if (client_credentials == Py_None) {
self->c_channel = grpc_channel_create(hostport, NULL);
return 0;
} else {
if (server_host_override == NULL) {
self->c_channel = grpc_secure_channel_create(
((ClientCredentials *)client_credentials)->c_client_credentials,
hostport, NULL);
} else {
server_host_override_arg.type = GRPC_ARG_STRING;
server_host_override_arg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG;
server_host_override_arg.value.string = server_host_override;
channel_args.num_args = 1;
channel_args.args = &server_host_override_arg;
self->c_channel = grpc_secure_channel_create(
((ClientCredentials *)client_credentials)->c_client_credentials,
hostport, &channel_args);
}
return 0;
}
}
static void pygrpc_channel_dealloc(Channel *self) {
if (self->c_channel != NULL) {
grpc_channel_destroy(self->c_channel);
}
self->ob_type->tp_free((PyObject *)self);
}
PyTypeObject pygrpc_ChannelType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_grpc.Channel", /*tp_name*/
sizeof(Channel), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_channel_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_channel.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pygrpc_channel_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
int pygrpc_add_channel(PyObject *module) {
if (PyType_Ready(&pygrpc_ChannelType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "Channel", (PyObject *)&pygrpc_ChannelType) ==
-1) {
return -1;
}
return 0;
}

@ -1,49 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__CHANNEL_H_
#define _ADAPTER__CHANNEL_H_
#include <Python.h>
#include <grpc/grpc.h>
typedef struct {
PyObject_HEAD
grpc_channel *c_channel;
} Channel;
extern PyTypeObject pygrpc_ChannelType;
int pygrpc_add_channel(PyObject *module);
#endif /* _ADAPTER__CHANNEL_H_ */

@ -1,121 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_client_credentials.h"
#include <Python.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
static int pygrpc_client_credentials_init(ClientCredentials *self,
PyObject *args, PyObject *kwds) {
char *root_certificates;
grpc_ssl_pem_key_cert_pair key_certificate_pair;
static char *kwlist[] = {"root_certificates", "private_key",
"certificate_chain", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "zzz:ClientCredentials", kwlist,
&root_certificates,
&key_certificate_pair.private_key,
&key_certificate_pair.cert_chain)) {
return -1;
}
if (key_certificate_pair.private_key != NULL && key_certificate_pair.cert_chain != NULL) {
self->c_client_credentials =
grpc_ssl_credentials_create(root_certificates, &key_certificate_pair);
} else {
self->c_client_credentials =
grpc_ssl_credentials_create(root_certificates, NULL);
}
return 0;
}
static void pygrpc_client_credentials_dealloc(ClientCredentials *self) {
if (self->c_client_credentials != NULL) {
grpc_credentials_release(self->c_client_credentials);
}
self->ob_type->tp_free((PyObject *)self);
}
PyTypeObject pygrpc_ClientCredentialsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_grpc.ClientCredencials", /*tp_name*/
sizeof(ClientCredentials), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_client_credentials_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_credentials.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pygrpc_client_credentials_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
int pygrpc_add_client_credentials(PyObject *module) {
if (PyType_Ready(&pygrpc_ClientCredentialsType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "ClientCredentials",
(PyObject *)&pygrpc_ClientCredentialsType) == -1) {
return -1;
}
return 0;
}

@ -1,49 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__CLIENT_CREDENTIALS_H_
#define _ADAPTER__CLIENT_CREDENTIALS_H_
#include <Python.h>
#include <grpc/grpc_security.h>
typedef struct {
PyObject_HEAD
grpc_credentials *c_client_credentials;
} ClientCredentials;
extern PyTypeObject pygrpc_ClientCredentialsType;
int pygrpc_add_client_credentials(PyObject *module);
#endif /* _ADAPTER__CLIENT_CREDENTIALS_H_ */

@ -1,653 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_completion_queue.h"
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include "grpc/_adapter/_call.h"
#include "grpc/_adapter/_tag.h"
static PyObject *status_class;
static PyObject *service_acceptance_class;
static PyObject *event_class;
static PyObject *ok_status_code;
static PyObject *cancelled_status_code;
static PyObject *unknown_status_code;
static PyObject *invalid_argument_status_code;
static PyObject *expired_status_code;
static PyObject *not_found_status_code;
static PyObject *already_exists_status_code;
static PyObject *permission_denied_status_code;
static PyObject *unauthenticated_status_code;
static PyObject *resource_exhausted_status_code;
static PyObject *failed_precondition_status_code;
static PyObject *aborted_status_code;
static PyObject *out_of_range_status_code;
static PyObject *unimplemented_status_code;
static PyObject *internal_error_status_code;
static PyObject *unavailable_status_code;
static PyObject *data_loss_status_code;
static PyObject *stop_event_kind;
static PyObject *write_event_kind;
static PyObject *complete_event_kind;
static PyObject *service_event_kind;
static PyObject *read_event_kind;
static PyObject *metadata_event_kind;
static PyObject *finish_event_kind;
static PyObject *pygrpc_as_py_time(gpr_timespec *timespec) {
return PyFloat_FromDouble(
timespec->tv_sec + ((double)timespec->tv_nsec) / 1.0E9);
}
static PyObject *pygrpc_status_code(grpc_status_code c_status_code) {
switch (c_status_code) {
case GRPC_STATUS_OK:
return ok_status_code;
case GRPC_STATUS_CANCELLED:
return cancelled_status_code;
case GRPC_STATUS_UNKNOWN:
return unknown_status_code;
case GRPC_STATUS_INVALID_ARGUMENT:
return invalid_argument_status_code;
case GRPC_STATUS_DEADLINE_EXCEEDED:
return expired_status_code;
case GRPC_STATUS_NOT_FOUND:
return not_found_status_code;
case GRPC_STATUS_ALREADY_EXISTS:
return already_exists_status_code;
case GRPC_STATUS_PERMISSION_DENIED:
return permission_denied_status_code;
case GRPC_STATUS_UNAUTHENTICATED:
return unauthenticated_status_code;
case GRPC_STATUS_RESOURCE_EXHAUSTED:
return resource_exhausted_status_code;
case GRPC_STATUS_FAILED_PRECONDITION:
return failed_precondition_status_code;
case GRPC_STATUS_ABORTED:
return aborted_status_code;
case GRPC_STATUS_OUT_OF_RANGE:
return out_of_range_status_code;
case GRPC_STATUS_UNIMPLEMENTED:
return unimplemented_status_code;
case GRPC_STATUS_INTERNAL:
return internal_error_status_code;
case GRPC_STATUS_UNAVAILABLE:
return unavailable_status_code;
case GRPC_STATUS_DATA_LOSS:
return data_loss_status_code;
default:
return NULL;
}
}
static PyObject *pygrpc_metadata_collection_get(
grpc_metadata *metadata_elements, size_t count) {
PyObject *metadata = PyList_New(count);
size_t i;
for (i = 0; i < count; ++i) {
grpc_metadata elem = metadata_elements[i];
PyObject *key = PyString_FromString(elem.key);
PyObject *value = PyString_FromStringAndSize(elem.value, elem.value_length);
PyObject* kvp = PyTuple_Pack(2, key, value);
/* n.b. PyList_SetItem *steals* a reference to the set element. */
PyList_SetItem(metadata, i, kvp);
Py_DECREF(key);
Py_DECREF(value);
}
return metadata;
}
static PyObject *pygrpc_stop_event_args(grpc_event *c_event) {
return PyTuple_Pack(8, stop_event_kind, Py_None, Py_None, Py_None,
Py_None, Py_None, Py_None, Py_None);
}
static PyObject *pygrpc_write_event_args(grpc_event *c_event) {
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
PyObject *write_accepted = Py_True;
return PyTuple_Pack(8, write_event_kind, user_tag,
write_accepted, Py_None, Py_None, Py_None, Py_None,
Py_None);
}
static PyObject *pygrpc_complete_event_args(grpc_event *c_event) {
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
PyObject *complete_accepted = Py_True;
return PyTuple_Pack(8, complete_event_kind, user_tag,
Py_None, complete_accepted, Py_None, Py_None, Py_None,
Py_None);
}
static PyObject *pygrpc_service_event_args(grpc_event *c_event) {
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
if (tag->call->call_details.method == NULL) {
return PyTuple_Pack(
8, service_event_kind, user_tag, Py_None, Py_None, Py_None, Py_None,
Py_None, Py_None);
} else {
PyObject *method = NULL;
PyObject *host = NULL;
PyObject *service_deadline = NULL;
PyObject *service_acceptance = NULL;
PyObject *metadata = NULL;
PyObject *event_args = NULL;
method = PyBytes_FromString(tag->call->call_details.method);
if (method == NULL) {
goto error;
}
host = PyBytes_FromString(tag->call->call_details.host);
if (host == NULL) {
goto error;
}
service_deadline =
pygrpc_as_py_time(&tag->call->call_details.deadline);
if (service_deadline == NULL) {
goto error;
}
service_acceptance =
PyObject_CallFunctionObjArgs(service_acceptance_class, tag->call,
method, host, service_deadline, NULL);
if (service_acceptance == NULL) {
goto error;
}
metadata = pygrpc_metadata_collection_get(
tag->call->recv_metadata.metadata,
tag->call->recv_metadata.count);
event_args = PyTuple_Pack(8, service_event_kind,
user_tag, Py_None, Py_None,
service_acceptance, Py_None, Py_None,
metadata);
Py_DECREF(service_acceptance);
Py_DECREF(metadata);
error:
Py_XDECREF(method);
Py_XDECREF(host);
Py_XDECREF(service_deadline);
return event_args;
}
}
static PyObject *pygrpc_read_event_args(grpc_event *c_event) {
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
if (tag->call->recv_message == NULL) {
return PyTuple_Pack(8, read_event_kind, user_tag,
Py_None, Py_None, Py_None, Py_None, Py_None, Py_None);
} else {
size_t length;
size_t offset;
grpc_byte_buffer_reader *reader;
gpr_slice slice;
char *c_bytes;
PyObject *bytes;
PyObject *event_args;
length = grpc_byte_buffer_length(tag->call->recv_message);
reader = grpc_byte_buffer_reader_create(tag->call->recv_message);
c_bytes = gpr_malloc(length);
offset = 0;
while (grpc_byte_buffer_reader_next(reader, &slice)) {
memcpy(c_bytes + offset, GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
offset += GPR_SLICE_LENGTH(slice);
}
grpc_byte_buffer_reader_destroy(reader);
bytes = PyBytes_FromStringAndSize(c_bytes, length);
gpr_free(c_bytes);
if (bytes == NULL) {
return NULL;
}
event_args = PyTuple_Pack(8, read_event_kind, user_tag,
Py_None, Py_None, Py_None, bytes, Py_None,
Py_None);
Py_DECREF(bytes);
return event_args;
}
}
static PyObject *pygrpc_metadata_event_args(grpc_event *c_event) {
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
PyObject *metadata = pygrpc_metadata_collection_get(
tag->call->recv_metadata.metadata,
tag->call->recv_metadata.count);
PyObject* result = PyTuple_Pack(
8, metadata_event_kind, user_tag, Py_None, Py_None,
Py_None, Py_None, Py_None, metadata);
Py_DECREF(metadata);
return result;
}
static PyObject *pygrpc_finished_server_event_args(grpc_event *c_event) {
PyObject *code;
PyObject *details;
PyObject *status;
PyObject *event_args;
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
code = pygrpc_status_code(tag->call->cancelled ? GRPC_STATUS_CANCELLED : GRPC_STATUS_OK);
if (code == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Unrecognized status code!");
return NULL;
}
details = PyBytes_FromString("");
if (details == NULL) {
return NULL;
}
status = PyObject_CallFunctionObjArgs(status_class, code, details, NULL);
Py_DECREF(details);
if (status == NULL) {
return NULL;
}
event_args = PyTuple_Pack(8, finish_event_kind, user_tag,
Py_None, Py_None, Py_None, Py_None, status,
Py_None);
Py_DECREF(status);
return event_args;
}
static PyObject *pygrpc_finished_client_event_args(grpc_event *c_event) {
PyObject *code;
PyObject *details;
PyObject *status;
PyObject *event_args;
PyObject *metadata;
pygrpc_tag *tag = (pygrpc_tag *)(c_event->tag);
PyObject *user_tag = tag->user_tag;
code = pygrpc_status_code(tag->call->status);
if (code == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Unrecognized status code!");
return NULL;
}
if (tag->call->status_details == NULL) {
details = PyBytes_FromString("");
} else {
details = PyBytes_FromString(tag->call->status_details);
}
if (details == NULL) {
return NULL;
}
status = PyObject_CallFunctionObjArgs(status_class, code, details, NULL);
Py_DECREF(details);
if (status == NULL) {
return NULL;
}
metadata = pygrpc_metadata_collection_get(
tag->call->recv_trailing_metadata.metadata,
tag->call->recv_trailing_metadata.count);
event_args = PyTuple_Pack(8, finish_event_kind, user_tag,
Py_None, Py_None, Py_None, Py_None, status,
metadata);
Py_DECREF(status);
Py_DECREF(metadata);
return event_args;
}
static int pygrpc_completion_queue_init(CompletionQueue *self, PyObject *args,
PyObject *kwds) {
static char *kwlist[] = {NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, ":CompletionQueue", kwlist)) {
return -1;
}
self->c_completion_queue = grpc_completion_queue_create();
return 0;
}
static void pygrpc_completion_queue_dealloc(CompletionQueue *self) {
grpc_completion_queue_destroy(self->c_completion_queue);
self->ob_type->tp_free((PyObject *)self);
}
static PyObject *pygrpc_completion_queue_get(CompletionQueue *self,
PyObject *args) {
PyObject *deadline;
double double_deadline;
gpr_timespec deadline_timespec;
grpc_event c_event;
PyObject *event_args;
PyObject *event;
pygrpc_tag *tag;
if (!(PyArg_ParseTuple(args, "O:get", &deadline))) {
return NULL;
}
if (deadline == Py_None) {
deadline_timespec = gpr_inf_future;
} else {
double_deadline = PyFloat_AsDouble(deadline);
if (PyErr_Occurred()) {
return NULL;
}
deadline_timespec = gpr_time_from_nanos((long)(double_deadline * 1.0E9));
}
/* TODO(nathaniel): Suppress clang-format in this block and remove the
unnecessary and unPythonic semicolons trailing the _ALLOW_THREADS macros.
(Right now clang-format only understands //-demarcated suppressions.) */
Py_BEGIN_ALLOW_THREADS;
c_event =
grpc_completion_queue_next(self->c_completion_queue, deadline_timespec);
Py_END_ALLOW_THREADS;
tag = (pygrpc_tag *)c_event.tag;
switch (c_event.type) {
case GRPC_QUEUE_TIMEOUT:
Py_RETURN_NONE;
break;
case GRPC_QUEUE_SHUTDOWN:
event_args = pygrpc_stop_event_args(&c_event);
break;
case GRPC_OP_COMPLETE: {
if (!tag) {
PyErr_SetString(PyExc_Exception, "Unrecognized event type!");
return NULL;
}
switch (tag->type) {
case PYGRPC_INITIAL_METADATA:
if (tag) {
pygrpc_tag_destroy(tag);
}
return pygrpc_completion_queue_get(self, args);
case PYGRPC_WRITE_ACCEPTED:
event_args = pygrpc_write_event_args(&c_event);
break;
case PYGRPC_FINISH_ACCEPTED:
event_args = pygrpc_complete_event_args(&c_event);
break;
case PYGRPC_SERVER_RPC_NEW:
event_args = pygrpc_service_event_args(&c_event);
break;
case PYGRPC_READ:
event_args = pygrpc_read_event_args(&c_event);
break;
case PYGRPC_CLIENT_METADATA_READ:
event_args = pygrpc_metadata_event_args(&c_event);
break;
case PYGRPC_FINISHED_CLIENT:
event_args = pygrpc_finished_client_event_args(&c_event);
break;
case PYGRPC_FINISHED_SERVER:
event_args = pygrpc_finished_server_event_args(&c_event);
break;
default:
PyErr_SetString(PyExc_Exception, "Unrecognized op event type!");
return NULL;
}
break;
}
default:
PyErr_SetString(PyExc_Exception, "Unrecognized event type!");
return NULL;
}
if (event_args == NULL) {
return NULL;
}
event = PyObject_CallObject(event_class, event_args);
Py_DECREF(event_args);
if (tag) {
pygrpc_tag_destroy(tag);
}
return event;
}
static PyObject *pygrpc_completion_queue_stop(CompletionQueue *self) {
grpc_completion_queue_shutdown(self->c_completion_queue);
Py_RETURN_NONE;
}
static PyMethodDef methods[] = {
{"get", (PyCFunction)pygrpc_completion_queue_get, METH_VARARGS,
"Get the next event."},
{"stop", (PyCFunction)pygrpc_completion_queue_stop, METH_NOARGS,
"Stop this completion queue."},
{NULL}};
PyTypeObject pygrpc_CompletionQueueType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_gprc.CompletionQueue", /*tp_name*/
sizeof(CompletionQueue), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_completion_queue_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_completion_queue.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pygrpc_completion_queue_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
static int pygrpc_get_status_codes(PyObject *datatypes_module) {
PyObject *code_class = PyObject_GetAttrString(datatypes_module, "Code");
if (code_class == NULL) {
return -1;
}
ok_status_code = PyObject_GetAttrString(code_class, "OK");
if (ok_status_code == NULL) {
return -1;
}
cancelled_status_code = PyObject_GetAttrString(code_class, "CANCELLED");
if (cancelled_status_code == NULL) {
return -1;
}
unknown_status_code = PyObject_GetAttrString(code_class, "UNKNOWN");
if (unknown_status_code == NULL) {
return -1;
}
invalid_argument_status_code =
PyObject_GetAttrString(code_class, "INVALID_ARGUMENT");
if (invalid_argument_status_code == NULL) {
return -1;
}
expired_status_code = PyObject_GetAttrString(code_class, "EXPIRED");
if (expired_status_code == NULL) {
return -1;
}
not_found_status_code = PyObject_GetAttrString(code_class, "NOT_FOUND");
if (not_found_status_code == NULL) {
return -1;
}
already_exists_status_code =
PyObject_GetAttrString(code_class, "ALREADY_EXISTS");
if (already_exists_status_code == NULL) {
return -1;
}
permission_denied_status_code =
PyObject_GetAttrString(code_class, "PERMISSION_DENIED");
if (permission_denied_status_code == NULL) {
return -1;
}
unauthenticated_status_code =
PyObject_GetAttrString(code_class, "UNAUTHENTICATED");
if (unauthenticated_status_code == NULL) {
return -1;
}
resource_exhausted_status_code =
PyObject_GetAttrString(code_class, "RESOURCE_EXHAUSTED");
if (resource_exhausted_status_code == NULL) {
return -1;
}
failed_precondition_status_code =
PyObject_GetAttrString(code_class, "FAILED_PRECONDITION");
if (failed_precondition_status_code == NULL) {
return -1;
}
aborted_status_code = PyObject_GetAttrString(code_class, "ABORTED");
if (aborted_status_code == NULL) {
return -1;
}
out_of_range_status_code = PyObject_GetAttrString(code_class, "OUT_OF_RANGE");
if (out_of_range_status_code == NULL) {
return -1;
}
unimplemented_status_code =
PyObject_GetAttrString(code_class, "UNIMPLEMENTED");
if (unimplemented_status_code == NULL) {
return -1;
}
internal_error_status_code =
PyObject_GetAttrString(code_class, "INTERNAL_ERROR");
if (internal_error_status_code == NULL) {
return -1;
}
unavailable_status_code = PyObject_GetAttrString(code_class, "UNAVAILABLE");
if (unavailable_status_code == NULL) {
return -1;
}
data_loss_status_code = PyObject_GetAttrString(code_class, "DATA_LOSS");
if (data_loss_status_code == NULL) {
return -1;
}
Py_DECREF(code_class);
return 0;
}
static int pygrpc_get_event_kinds(PyObject *event_class) {
PyObject *kind_class = PyObject_GetAttrString(event_class, "Kind");
if (kind_class == NULL) {
return -1;
}
stop_event_kind = PyObject_GetAttrString(kind_class, "STOP");
if (stop_event_kind == NULL) {
return -1;
}
write_event_kind = PyObject_GetAttrString(kind_class, "WRITE_ACCEPTED");
if (write_event_kind == NULL) {
return -1;
}
complete_event_kind = PyObject_GetAttrString(kind_class, "COMPLETE_ACCEPTED");
if (complete_event_kind == NULL) {
return -1;
}
service_event_kind = PyObject_GetAttrString(kind_class, "SERVICE_ACCEPTED");
if (service_event_kind == NULL) {
return -1;
}
read_event_kind = PyObject_GetAttrString(kind_class, "READ_ACCEPTED");
if (read_event_kind == NULL) {
return -1;
}
metadata_event_kind = PyObject_GetAttrString(kind_class, "METADATA_ACCEPTED");
if (metadata_event_kind == NULL) {
return -1;
}
finish_event_kind = PyObject_GetAttrString(kind_class, "FINISH");
if (finish_event_kind == NULL) {
return -1;
}
Py_DECREF(kind_class);
return 0;
}
int pygrpc_add_completion_queue(PyObject *module) {
char *datatypes_module_path = "grpc._adapter._datatypes";
PyObject *datatypes_module = PyImport_ImportModule(datatypes_module_path);
if (datatypes_module == NULL) {
return -1;
}
status_class = PyObject_GetAttrString(datatypes_module, "Status");
service_acceptance_class =
PyObject_GetAttrString(datatypes_module, "ServiceAcceptance");
event_class = PyObject_GetAttrString(datatypes_module, "Event");
if (status_class == NULL || service_acceptance_class == NULL ||
event_class == NULL) {
return -1;
}
if (pygrpc_get_status_codes(datatypes_module) == -1) {
return -1;
}
if (pygrpc_get_event_kinds(event_class) == -1) {
return -1;
}
Py_DECREF(datatypes_module);
if (PyType_Ready(&pygrpc_CompletionQueueType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "CompletionQueue",
(PyObject *)&pygrpc_CompletionQueueType) == -1) {
return -1;
}
return 0;
}

@ -1,86 +0,0 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Datatypes passed between Python and C code."""
import collections
import enum
@enum.unique
class Code(enum.IntEnum):
"""One Platform error codes (see status.h and codes.proto)."""
OK = 0
CANCELLED = 1
UNKNOWN = 2
INVALID_ARGUMENT = 3
EXPIRED = 4
NOT_FOUND = 5
ALREADY_EXISTS = 6
PERMISSION_DENIED = 7
UNAUTHENTICATED = 16
RESOURCE_EXHAUSTED = 8
FAILED_PRECONDITION = 9
ABORTED = 10
OUT_OF_RANGE = 11
UNIMPLEMENTED = 12
INTERNAL_ERROR = 13
UNAVAILABLE = 14
DATA_LOSS = 15
class Status(collections.namedtuple('Status', ['code', 'details'])):
"""Describes an RPC's overall status."""
class ServiceAcceptance(
collections.namedtuple(
'ServiceAcceptance', ['call', 'method', 'host', 'deadline'])):
"""Describes an RPC on the service side at the start of service."""
class Event(
collections.namedtuple(
'Event',
['kind', 'tag', 'write_accepted', 'complete_accepted',
'service_acceptance', 'bytes', 'status', 'metadata'])):
"""Describes an event emitted from a completion queue."""
@enum.unique
class Kind(enum.Enum):
"""Describes the kind of an event."""
STOP = object()
WRITE_ACCEPTED = object()
COMPLETE_ACCEPTED = object()
SERVICE_ACCEPTED = object()
READ_ACCEPTED = object()
METADATA_ACCEPTED = object()
FINISH = object()

@ -1,79 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_error.h"
#include <Python.h>
#include <grpc/grpc.h>
const PyObject *pygrpc_translate_call_error(grpc_call_error call_error) {
switch (call_error) {
case GRPC_CALL_OK:
Py_RETURN_NONE;
case GRPC_CALL_ERROR:
PyErr_SetString(PyExc_Exception, "Defect: unknown defect!");
return NULL;
case GRPC_CALL_ERROR_NOT_ON_SERVER:
PyErr_SetString(PyExc_Exception,
"Defect: client-only method called on server!");
return NULL;
case GRPC_CALL_ERROR_NOT_ON_CLIENT:
PyErr_SetString(PyExc_Exception,
"Defect: server-only method called on client!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_ACCEPTED:
PyErr_SetString(PyExc_Exception,
"Defect: attempted to accept already-accepted call!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_INVOKED:
PyErr_SetString(PyExc_Exception,
"Defect: attempted to invoke already-invoked call!");
return NULL;
case GRPC_CALL_ERROR_NOT_INVOKED:
PyErr_SetString(PyExc_Exception, "Defect: Call not yet invoked!");
return NULL;
case GRPC_CALL_ERROR_ALREADY_FINISHED:
PyErr_SetString(PyExc_Exception, "Defect: Call already finished!");
return NULL;
case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS:
PyErr_SetString(PyExc_Exception,
"Defect: Attempted extra read or extra write on call!");
return NULL;
case GRPC_CALL_ERROR_INVALID_FLAGS:
PyErr_SetString(PyExc_Exception, "Defect: invalid flags!");
return NULL;
default:
PyErr_SetString(PyExc_Exception, "Defect: Unknown call error!");
return NULL;
}
}

@ -1,42 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__ERROR_H_
#define _ADAPTER__ERROR_H_
#include <Python.h>
#include <grpc/grpc.h>
const PyObject *pygrpc_translate_call_error(grpc_call_error call_error);
#endif /* _ADAPTER__ERROR_H_ */

@ -0,0 +1,258 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Temporary old _low-like layer.
Eases refactoring burden while we overhaul the Python framework.
Plan:
The layers used to look like:
... # outside _adapter
fore.py + rear.py # visible outside _adapter
_low
_c
The layers currently look like:
... # outside _adapter
fore.py + rear.py # visible outside _adapter
_low_intermediary # adapter for new '_low' to old '_low'
_low # new '_low'
_c # new '_c'
We will later remove _low_intermediary after refactoring of fore.py and
rear.py according to the ticket system refactoring and get:
... # outside _adapter, refactored
fore.py + rear.py # visible outside _adapter, refactored
_low # new '_low'
_c # new '_c'
"""
import collections
import enum
from grpc._adapter import _low
from grpc._adapter import _types
_IGNORE_ME_TAG = object()
Code = _types.StatusCode
class Status(collections.namedtuple('Status', ['code', 'details'])):
"""Describes an RPC's overall status."""
class ServiceAcceptance(
collections.namedtuple(
'ServiceAcceptance', ['call', 'method', 'host', 'deadline'])):
"""Describes an RPC on the service side at the start of service."""
class Event(
collections.namedtuple(
'Event',
['kind', 'tag', 'write_accepted', 'complete_accepted',
'service_acceptance', 'bytes', 'status', 'metadata'])):
"""Describes an event emitted from a completion queue."""
@enum.unique
class Kind(enum.Enum):
"""Describes the kind of an event."""
STOP = object()
WRITE_ACCEPTED = object()
COMPLETE_ACCEPTED = object()
SERVICE_ACCEPTED = object()
READ_ACCEPTED = object()
METADATA_ACCEPTED = object()
FINISH = object()
class _TagAdapter(collections.namedtuple('_TagAdapter', [
'user_tag',
'kind'
])):
pass
class Call(object):
"""Adapter from old _low.Call interface to new _low.Call."""
def __init__(self, channel, completion_queue, method, host, deadline):
self._internal = channel._internal.create_call(
completion_queue._internal, method, host, deadline)
self._metadata = []
@staticmethod
def _from_internal(internal):
call = Call.__new__(Call)
call._internal = internal
call._metadata = []
return call
def invoke(self, completion_queue, metadata_tag, finish_tag):
err0 = self._internal.start_batch([
_types.OpArgs.send_initial_metadata(self._metadata)
], _IGNORE_ME_TAG)
err1 = self._internal.start_batch([
_types.OpArgs.recv_initial_metadata()
], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED))
err2 = self._internal.start_batch([
_types.OpArgs.recv_status_on_client()
], _TagAdapter(finish_tag, Event.Kind.FINISH))
return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK
def write(self, message, tag):
return self._internal.start_batch([
_types.OpArgs.send_message(message)
], _TagAdapter(tag, Event.Kind.WRITE_ACCEPTED))
def complete(self, tag):
return self._internal.start_batch([
_types.OpArgs.send_close_from_client()
], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED))
def accept(self, completion_queue, tag):
return self._internal.start_batch([
_types.OpArgs.recv_close_on_server()
], _TagAdapter(tag, Event.Kind.FINISH))
def add_metadata(self, key, value):
self._metadata.append((key, value))
def premetadata(self):
return self._internal.start_batch([
_types.OpArgs.send_initial_metadata(self._metadata)
], _IGNORE_ME_TAG)
self._metadata = []
def read(self, tag):
return self._internal.start_batch([
_types.OpArgs.recv_message()
], _TagAdapter(tag, Event.Kind.READ_ACCEPTED))
def status(self, status, tag):
return self._internal.start_batch([
_types.OpArgs.send_status_from_server(self._metadata, status.code, status.details)
], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED))
def cancel(self):
return self._internal.cancel()
class Channel(object):
"""Adapter from old _low.Channel interface to new _low.Channel."""
def __init__(self, hostport, client_credentials, server_host_override=None):
args = []
if server_host_override:
args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override))
creds = None
if client_credentials:
creds = client_credentials._internal
self._internal = _low.Channel(hostport, args, creds)
class CompletionQueue(object):
"""Adapter from old _low.CompletionQueue interface to new _low.CompletionQueue."""
def __init__(self):
self._internal = _low.CompletionQueue()
def get(self, deadline=None):
if deadline is None:
ev = self._internal.next()
else:
ev = self._internal.next(deadline)
if ev is None:
return None
elif ev.tag is _IGNORE_ME_TAG:
return self.get(deadline)
elif ev.type == _types.EventType.QUEUE_SHUTDOWN:
kind = Event.Kind.STOP
tag = None
write_accepted = None
complete_accepted = None
service_acceptance = None
message_bytes = None
status = None
metadata = None
elif ev.type == _types.EventType.OP_COMPLETE:
kind = ev.tag.kind
tag = ev.tag.user_tag
write_accepted = ev.success if kind == Event.Kind.WRITE_ACCEPTED else None
complete_accepted = ev.success if kind == Event.Kind.COMPLETE_ACCEPTED else None
service_acceptance = ServiceAcceptance(Call._from_internal(ev.call), ev.call_details.method, ev.call_details.host, ev.call_details.deadline) if kind == Event.Kind.SERVICE_ACCEPTED else None
message_bytes = ev.results[0].message if kind == Event.Kind.READ_ACCEPTED else None
status = Status(ev.results[0].status.code, ev.results[0].status.details) if (kind == Event.Kind.FINISH and ev.results[0].status) else Status(_types.StatusCode.CANCELLED if ev.results[0].cancelled else _types.StatusCode.OK, '') if ev.results[0].cancelled is not None else None
metadata = ev.results[0].initial_metadata if (kind in [Event.Kind.SERVICE_ACCEPTED, Event.Kind.METADATA_ACCEPTED]) else (ev.results[0].trailing_metadata if kind == Event.Kind.FINISH else None)
else:
raise RuntimeError('unknown event')
result_ev = Event(kind=kind, tag=tag, write_accepted=write_accepted, complete_accepted=complete_accepted, service_acceptance=service_acceptance, bytes=message_bytes, status=status, metadata=metadata)
return result_ev
def stop(self):
self._internal.shutdown()
class Server(object):
"""Adapter from old _low.Server interface to new _low.Server."""
def __init__(self, completion_queue):
self._internal = _low.Server(completion_queue._internal, [])
self._internal_cq = completion_queue._internal
def add_http2_addr(self, addr):
return self._internal.add_http2_port(addr)
def add_secure_http2_addr(self, addr, server_credentials):
if server_credentials is None:
return self._internal.add_http2_port(addr, None)
else:
return self._internal.add_http2_port(addr, server_credentials._internal)
def start(self):
return self._internal.start()
def service(self, tag):
return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED))
def stop(self):
return self._internal.shutdown()
class ClientCredentials(object):
"""Adapter from old _low.ClientCredentials interface to new _low.ClientCredentials."""
def __init__(self, root_certificates, private_key, certificate_chain):
self._internal = _low.ClientCredentials.ssl(root_certificates, private_key, certificate_chain)
class ServerCredentials(object):
"""Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials."""
def __init__(self, root_credentials, pair_sequence):
self._internal = _low.ServerCredentials.ssl(root_credentials, list(pair_sequence))

@ -0,0 +1,421 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tests for the old '_low'."""
import time
import unittest
from grpc._adapter import _intermediary_low as _low
_STREAM_LENGTH = 300
_TIMEOUT = 5
_AFTER_DELAY = 2
_FUTURE = time.time() + 60 * 60 * 24
_BYTE_SEQUENCE = b'\abcdefghijklmnopqrstuvwxyz0123456789' * 200
_BYTE_SEQUENCE_SEQUENCE = tuple(
bytes(bytearray((row + column) % 256 for column in range(row)))
for row in range(_STREAM_LENGTH))
class LonelyClientTest(unittest.TestCase):
def testLonelyClient(self):
host = 'nosuchhostexists'
port = 54321
method = 'test method'
deadline = time.time() + _TIMEOUT
after_deadline = deadline + _AFTER_DELAY
metadata_tag = object()
finish_tag = object()
completion_queue = _low.CompletionQueue()
channel = _low.Channel('%s:%d' % (host, port), None)
client_call = _low.Call(channel, completion_queue, method, host, deadline)
client_call.invoke(completion_queue, metadata_tag, finish_tag)
first_event = completion_queue.get(after_deadline)
self.assertIsNotNone(first_event)
second_event = completion_queue.get(after_deadline)
self.assertIsNotNone(second_event)
kinds = [event.kind for event in (first_event, second_event)]
self.assertItemsEqual(
(_low.Event.Kind.METADATA_ACCEPTED, _low.Event.Kind.FINISH),
kinds)
self.assertIsNone(completion_queue.get(after_deadline))
completion_queue.stop()
stop_event = completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.STOP, stop_event.kind)
del client_call
del channel
del completion_queue
class EchoTest(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()
# NOTE(nathaniel): Yep, this is weird; it's a consequence of
# grpc_server_destroy's being what has the effect of telling the server's
# completion queue to pump out all pending events/tags immediately rather
# than gracefully completing all outstanding RPCs while accepting no new
# ones.
# TODO(nathaniel): Deallocation of a Python object shouldn't have this kind
# of observable side effect let alone such an important one.
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
self.server_completion_queue = None
self.client_completion_queue = None
def _perform_echo_test(self, test_data):
method = 'test method'
details = 'test details'
server_leading_metadata_key = 'my_server_leading_key'
server_leading_metadata_value = 'my_server_leading_value'
server_trailing_metadata_key = 'my_server_trailing_key'
server_trailing_metadata_value = 'my_server_trailing_value'
client_metadata_key = 'my_client_key'
client_metadata_value = 'my_client_value'
server_leading_binary_metadata_key = 'my_server_leading_key-bin'
server_leading_binary_metadata_value = b'\0'*2047
server_trailing_binary_metadata_key = 'my_server_trailing_key-bin'
server_trailing_binary_metadata_value = b'\0'*2047
client_binary_metadata_key = 'my_client_key-bin'
client_binary_metadata_value = b'\0'*2047
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
complete_tag = object()
service_tag = object()
read_tag = object()
status_tag = object()
server_data = []
client_data = []
client_call = _low.Call(self.channel, self.client_completion_queue,
method, self.host, deadline)
client_call.add_metadata(client_metadata_key, client_metadata_value)
client_call.add_metadata(client_binary_metadata_key,
client_binary_metadata_value)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(service_accepted)
self.assertIs(service_accepted.kind, _low.Event.Kind.SERVICE_ACCEPTED)
self.assertIs(service_accepted.tag, service_tag)
self.assertEqual(method, service_accepted.service_acceptance.method)
self.assertEqual(self.host, service_accepted.service_acceptance.host)
self.assertIsNotNone(service_accepted.service_acceptance.call)
metadata = dict(service_accepted.metadata)
self.assertIn(client_metadata_key, metadata)
self.assertEqual(client_metadata_value, metadata[client_metadata_key])
self.assertIn(client_binary_metadata_key, metadata)
self.assertEqual(client_binary_metadata_value,
metadata[client_binary_metadata_key])
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.add_metadata(server_leading_metadata_key,
server_leading_metadata_value)
server_call.add_metadata(server_leading_binary_metadata_key,
server_leading_binary_metadata_value)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
self.assertEqual(_low.Event.Kind.METADATA_ACCEPTED, metadata_accepted.kind)
self.assertEqual(metadata_tag, metadata_accepted.tag)
metadata = dict(metadata_accepted.metadata)
self.assertIn(server_leading_metadata_key, metadata)
self.assertEqual(server_leading_metadata_value,
metadata[server_leading_metadata_key])
self.assertIn(server_leading_binary_metadata_key, metadata)
self.assertEqual(server_leading_binary_metadata_value,
metadata[server_leading_binary_metadata_key])
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED)
self.assertIs(write_accepted.tag, write_tag)
self.assertIs(write_accepted.write_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind)
self.assertEqual(write_tag, write_accepted.tag)
self.assertTrue(write_accepted.write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
client_data.append(read_accepted.bytes)
client_call.complete(complete_tag)
complete_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(complete_accepted)
self.assertIs(complete_accepted.kind, _low.Event.Kind.COMPLETE_ACCEPTED)
self.assertIs(complete_accepted.tag, complete_tag)
self.assertIs(complete_accepted.complete_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
server_call.add_metadata(server_trailing_metadata_key,
server_trailing_metadata_value)
server_call.add_metadata(server_trailing_binary_metadata_key,
server_trailing_binary_metadata_value)
server_call.status(_low.Status(_low.Code.OK, details), status_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.COMPLETE_ACCEPTED:
status_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
status_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(status_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.COMPLETE_ACCEPTED, status_accepted.kind)
self.assertEqual(status_tag, status_accepted.tag)
self.assertTrue(status_accepted.complete_accepted)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(finish_tag, rpc_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, ''), rpc_accepted.status)
client_call.read(read_tag)
client_terminal_event_one = self.client_completion_queue.get(_FUTURE)
client_terminal_event_two = self.client_completion_queue.get(_FUTURE)
if client_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = client_terminal_event_one
finish_accepted = client_terminal_event_two
else:
read_accepted = client_terminal_event_two
finish_accepted = client_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(finish_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, finish_accepted.kind)
self.assertEqual(finish_tag, finish_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, details), finish_accepted.status)
metadata = dict(finish_accepted.metadata)
self.assertIn(server_trailing_metadata_key, metadata)
self.assertEqual(server_trailing_metadata_value,
metadata[server_trailing_metadata_key])
self.assertIn(server_trailing_binary_metadata_key, metadata)
self.assertEqual(server_trailing_binary_metadata_value,
metadata[server_trailing_binary_metadata_key])
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
def testNoEcho(self):
self._perform_echo_test(())
def testOneByteEcho(self):
self._perform_echo_test([b'\x07'])
def testOneManyByteEcho(self):
self._perform_echo_test([_BYTE_SEQUENCE])
def testManyOneByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE)
def testManyManyByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE_SEQUENCE)
class CancellationTest(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
def testCancellation(self):
method = 'test method'
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
service_tag = object()
read_tag = object()
test_data = _BYTE_SEQUENCE_SEQUENCE
server_data = []
client_data = []
client_call = _low.Call(self.channel, self.client_completion_queue,
method, self.host, deadline)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
client_data.append(read_accepted.bytes)
client_call.cancel()
# cancel() is idempotent.
client_call.cancel()
client_call.cancel()
client_call.cancel()
server_call.read(read_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
read_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, ''), rpc_accepted.status)
finish_event = self.client_completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.FINISH, finish_event.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'),
finish_event.status)
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
class ExpirationTest(unittest.TestCase):
@unittest.skip('TODO(nathaniel): Expiration test!')
def testExpiration(self):
pass
if __name__ == '__main__':
unittest.main(verbosity=2)

@ -27,31 +27,85 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""A Python interface for GRPC C core structures and behaviors."""
import atexit
import gc
from grpc._adapter import _c
from grpc._adapter import _datatypes
def _shut_down():
# force garbage collection before shutting down grpc, to ensure all grpc
# objects are cleaned up
gc.collect()
_c.shut_down()
_c.init()
atexit.register(_shut_down)
# pylint: disable=invalid-name
Code = _datatypes.Code
Status = _datatypes.Status
Event = _datatypes.Event
Call = _c.Call
Channel = _c.Channel
CompletionQueue = _c.CompletionQueue
Server = _c.Server
from grpc._adapter import _types
ClientCredentials = _c.ClientCredentials
ServerCredentials = _c.ServerCredentials
# pylint: enable=invalid-name
class CompletionQueue(_types.CompletionQueue):
def __init__(self):
self.completion_queue = _c.CompletionQueue()
def next(self, deadline=float('+inf')):
raw_event = self.completion_queue.next(deadline)
if raw_event is None:
return None
event = _types.Event(*raw_event)
if event.call is not None:
event = event._replace(call=Call(event.call))
if event.call_details is not None:
event = event._replace(call_details=_types.CallDetails(*event.call_details))
if event.results is not None:
new_results = [_types.OpResult(*r) for r in event.results]
new_results = [r if r.status is None else r._replace(status=_types.Status(_types.StatusCode(r.status[0]), r.status[1])) for r in new_results]
event = event._replace(results=new_results)
return event
def shutdown(self):
self.completion_queue.shutdown()
class Call(_types.Call):
def __init__(self, call):
self.call = call
def start_batch(self, ops, tag):
return self.call.start_batch(ops, tag)
def cancel(self, code=None, details=None):
if code is None and details is None:
return self.call.cancel()
else:
return self.call.cancel(code, details)
class Channel(_types.Channel):
def __init__(self, target, args, creds=None):
if creds is None:
self.channel = _c.Channel(target, args)
else:
self.channel = _c.Channel(target, args, creds)
def create_call(self, completion_queue, method, host, deadline=None):
return Call(self.channel.create_call(completion_queue.completion_queue, method, host, deadline))
_NO_TAG = object()
class Server(_types.Server):
def __init__(self, completion_queue, args):
self.server = _c.Server(completion_queue.completion_queue, args)
def add_http2_port(self, addr, creds=None):
if creds is None:
return self.server.add_http2_port(addr)
else:
return self.server.add_http2_port(addr, creds)
def start(self):
return self.server.start()
def shutdown(self, tag=_NO_TAG):
if tag is _NO_TAG:
return self.server.shutdown()
else:
return self.server.shutdown(tag)
def request_call(self, completion_queue, tag):
return self.server.request_call(completion_queue.completion_queue, tag)

@ -27,388 +27,141 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Tests for _adapter._low."""
import time
import unittest
from grpc._adapter import _types
from grpc._adapter import _low
_STREAM_LENGTH = 300
_TIMEOUT = 5
_AFTER_DELAY = 2
_FUTURE = time.time() + 60 * 60 * 24
_BYTE_SEQUENCE = b'\abcdefghijklmnopqrstuvwxyz0123456789' * 200
_BYTE_SEQUENCE_SEQUENCE = tuple(
bytes(bytearray((row + column) % 256 for column in range(row)))
for row in range(_STREAM_LENGTH))
class LonelyClientTest(unittest.TestCase):
def testLonelyClient(self):
host = 'nosuchhostexists'
port = 54321
method = 'test method'
deadline = time.time() + _TIMEOUT
after_deadline = deadline + _AFTER_DELAY
metadata_tag = object()
finish_tag = object()
completion_queue = _low.CompletionQueue()
channel = _low.Channel('%s:%d' % (host, port), None)
client_call = _low.Call(channel, completion_queue, method, host, deadline)
client_call.invoke(completion_queue, metadata_tag, finish_tag)
first_event = completion_queue.get(after_deadline)
self.assertIsNotNone(first_event)
second_event = completion_queue.get(after_deadline)
self.assertIsNotNone(second_event)
kinds = [event.kind for event in (first_event, second_event)]
self.assertItemsEqual(
(_low.Event.Kind.METADATA_ACCEPTED, _low.Event.Kind.FINISH),
kinds)
self.assertIsNone(completion_queue.get(after_deadline))
completion_queue.stop()
stop_event = completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.STOP, stop_event.kind)
class EchoTest(unittest.TestCase):
class InsecureServerInsecureClient(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.server = _low.Server(self.server_completion_queue, [])
self.port = self.server.add_http2_port('[::]:0')
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()
# NOTE(nathaniel): Yep, this is weird; it's a consequence of
# grpc_server_destroy's being what has the effect of telling the server's
# completion queue to pump out all pending events/tags immediately rather
# than gracefully completing all outstanding RPCs while accepting no new
# ones.
# TODO(nathaniel): Deallocation of a Python object shouldn't have this kind
# of observable side effect let alone such an important one.
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(_FUTURE)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
self.server_completion_queue = None
self.client_completion_queue = None
def _perform_echo_test(self, test_data):
method = 'test method'
details = 'test details'
server_leading_metadata_key = 'my_server_leading_key'
server_leading_metadata_value = 'my_server_leading_value'
server_trailing_metadata_key = 'my_server_trailing_key'
server_trailing_metadata_value = 'my_server_trailing_value'
client_metadata_key = 'my_client_key'
client_metadata_value = 'my_client_value'
server_leading_binary_metadata_key = 'my_server_leading_key-bin'
server_leading_binary_metadata_value = b'\0'*2047
server_trailing_binary_metadata_key = 'my_server_trailing_key-bin'
server_trailing_binary_metadata_value = b'\0'*2047
client_binary_metadata_key = 'my_client_key-bin'
client_binary_metadata_value = b'\0'*2047
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
complete_tag = object()
service_tag = object()
read_tag = object()
status_tag = object()
server_data = []
client_data = []
client_call = _low.Call(self.channel, self.client_completion_queue,
method, self.host, deadline)
client_call.add_metadata(client_metadata_key, client_metadata_value)
client_call.add_metadata(client_binary_metadata_key,
client_binary_metadata_value)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(service_accepted)
self.assertIs(service_accepted.kind, _low.Event.Kind.SERVICE_ACCEPTED)
self.assertIs(service_accepted.tag, service_tag)
self.assertEqual(method, service_accepted.service_acceptance.method)
self.assertEqual(self.host, service_accepted.service_acceptance.host)
self.assertIsNotNone(service_accepted.service_acceptance.call)
metadata = dict(service_accepted.metadata)
self.assertIn(client_metadata_key, metadata)
self.assertEqual(client_metadata_value, metadata[client_metadata_key])
self.assertIn(client_binary_metadata_key, metadata)
self.assertEqual(client_binary_metadata_value,
metadata[client_binary_metadata_key])
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.add_metadata(server_leading_metadata_key,
server_leading_metadata_value)
server_call.add_metadata(server_leading_binary_metadata_key,
server_leading_binary_metadata_value)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
self.assertEqual(_low.Event.Kind.METADATA_ACCEPTED, metadata_accepted.kind)
self.assertEqual(metadata_tag, metadata_accepted.tag)
metadata = dict(metadata_accepted.metadata)
self.assertIn(server_leading_metadata_key, metadata)
self.assertEqual(server_leading_metadata_value,
metadata[server_leading_metadata_key])
self.assertIn(server_leading_binary_metadata_key, metadata)
self.assertEqual(server_leading_binary_metadata_value,
metadata[server_leading_binary_metadata_key])
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertIs(write_accepted.kind, _low.Event.Kind.WRITE_ACCEPTED)
self.assertIs(write_accepted.tag, write_tag)
self.assertIs(write_accepted.write_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
self.assertEqual(_low.Event.Kind.WRITE_ACCEPTED, write_accepted.kind)
self.assertEqual(write_tag, write_accepted.tag)
self.assertTrue(write_accepted.write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNotNone(read_accepted.bytes)
client_data.append(read_accepted.bytes)
client_call.complete(complete_tag)
complete_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(complete_accepted)
self.assertIs(complete_accepted.kind, _low.Event.Kind.COMPLETE_ACCEPTED)
self.assertIs(complete_accepted.tag, complete_tag)
self.assertIs(complete_accepted.complete_accepted, True)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(read_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
server_call.add_metadata(server_trailing_metadata_key,
server_trailing_metadata_value)
server_call.add_metadata(server_trailing_binary_metadata_key,
server_trailing_binary_metadata_value)
server_call.status(_low.Status(_low.Code.OK, details), status_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.COMPLETE_ACCEPTED:
status_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
status_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(status_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.COMPLETE_ACCEPTED, status_accepted.kind)
self.assertEqual(status_tag, status_accepted.tag)
self.assertTrue(status_accepted.complete_accepted)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(finish_tag, rpc_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, ''), rpc_accepted.status)
client_call.read(read_tag)
client_terminal_event_one = self.client_completion_queue.get(_FUTURE)
client_terminal_event_two = self.client_completion_queue.get(_FUTURE)
if client_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = client_terminal_event_one
finish_accepted = client_terminal_event_two
else:
read_accepted = client_terminal_event_two
finish_accepted = client_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(finish_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertEqual(read_tag, read_accepted.tag)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, finish_accepted.kind)
self.assertEqual(finish_tag, finish_accepted.tag)
self.assertEqual(_low.Status(_low.Code.OK, details), finish_accepted.status)
metadata = dict(finish_accepted.metadata)
self.assertIn(server_trailing_metadata_key, metadata)
self.assertEqual(server_trailing_metadata_value,
metadata[server_trailing_metadata_key])
self.assertIn(server_trailing_binary_metadata_key, metadata)
self.assertEqual(server_trailing_binary_metadata_value,
metadata[server_trailing_binary_metadata_key])
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
self.client_channel = _low.Channel('localhost:%d'%self.port, [])
def testNoEcho(self):
self._perform_echo_test(())
def testOneByteEcho(self):
self._perform_echo_test([b'\x07'])
def testOneManyByteEcho(self):
self._perform_echo_test([_BYTE_SEQUENCE])
def testManyOneByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE)
def testManyManyByteEchoes(self):
self._perform_echo_test(_BYTE_SEQUENCE_SEQUENCE)
class CancellationTest(unittest.TestCase):
def setUp(self):
self.host = 'localhost'
self.server_completion_queue = _low.CompletionQueue()
self.server = _low.Server(self.server_completion_queue)
port = self.server.add_http2_addr('[::]:0')
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()
self.server.shutdown()
del self.client_channel
del self.server
self.server_completion_queue.stop()
self.client_completion_queue.stop()
while True:
event = self.server_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
while True:
event = self.client_completion_queue.get(0)
if event is not None and event.kind is _low.Event.Kind.STOP:
break
def testCancellation(self):
method = 'test method'
deadline = _FUTURE
metadata_tag = object()
finish_tag = object()
write_tag = object()
service_tag = object()
read_tag = object()
test_data = _BYTE_SEQUENCE_SEQUENCE
server_data = []
client_data = []
client_call = _low.Call(self.channel, self.client_completion_queue,
method, self.host, deadline)
client_call.invoke(self.client_completion_queue, metadata_tag, finish_tag)
self.server.service(service_tag)
service_accepted = self.server_completion_queue.get(_FUTURE)
server_call = service_accepted.service_acceptance.call
server_call.accept(self.server_completion_queue, finish_tag)
server_call.premetadata()
metadata_accepted = self.client_completion_queue.get(_FUTURE)
self.assertIsNotNone(metadata_accepted)
for datum in test_data:
client_call.write(datum, write_tag)
write_accepted = self.client_completion_queue.get(_FUTURE)
server_call.read(read_tag)
read_accepted = self.server_completion_queue.get(_FUTURE)
server_data.append(read_accepted.bytes)
server_call.write(read_accepted.bytes, write_tag)
write_accepted = self.server_completion_queue.get(_FUTURE)
self.assertIsNotNone(write_accepted)
client_call.read(read_tag)
read_accepted = self.client_completion_queue.get(_FUTURE)
client_data.append(read_accepted.bytes)
client_call.cancel()
# cancel() is idempotent.
client_call.cancel()
client_call.cancel()
client_call.cancel()
server_call.read(read_tag)
server_terminal_event_one = self.server_completion_queue.get(_FUTURE)
server_terminal_event_two = self.server_completion_queue.get(_FUTURE)
if server_terminal_event_one.kind == _low.Event.Kind.READ_ACCEPTED:
read_accepted = server_terminal_event_one
rpc_accepted = server_terminal_event_two
else:
read_accepted = server_terminal_event_two
rpc_accepted = server_terminal_event_one
self.assertIsNotNone(read_accepted)
self.assertIsNotNone(rpc_accepted)
self.assertEqual(_low.Event.Kind.READ_ACCEPTED, read_accepted.kind)
self.assertIsNone(read_accepted.bytes)
self.assertEqual(_low.Event.Kind.FINISH, rpc_accepted.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, ''), rpc_accepted.status)
finish_event = self.client_completion_queue.get(_FUTURE)
self.assertEqual(_low.Event.Kind.FINISH, finish_event.kind)
self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'),
finish_event.status)
server_timeout_none_event = self.server_completion_queue.get(0)
self.assertIsNone(server_timeout_none_event)
client_timeout_none_event = self.client_completion_queue.get(0)
self.assertIsNone(client_timeout_none_event)
self.assertSequenceEqual(test_data, server_data)
self.assertSequenceEqual(test_data, client_data)
class ExpirationTest(unittest.TestCase):
@unittest.skip('TODO(nathaniel): Expiration test!')
def testExpiration(self):
pass
self.client_completion_queue.shutdown()
while self.client_completion_queue.next().type != _types.EventType.QUEUE_SHUTDOWN:
pass
self.server_completion_queue.shutdown()
while self.server_completion_queue.next().type != _types.EventType.QUEUE_SHUTDOWN:
pass
del self.client_completion_queue
del self.server_completion_queue
def testEcho(self):
DEADLINE = time.time()+5
DEADLINE_TOLERANCE = 0.25
CLIENT_METADATA_ASCII_KEY = 'key'
CLIENT_METADATA_ASCII_VALUE = 'val'
CLIENT_METADATA_BIN_KEY = 'key-bin'
CLIENT_METADATA_BIN_VALUE = b'\0'*1000
SERVER_INITIAL_METADATA_KEY = 'init_me_me_me'
SERVER_INITIAL_METADATA_VALUE = 'whodawha?'
SERVER_TRAILING_METADATA_KEY = 'California_is_in_a_drought'
SERVER_TRAILING_METADATA_VALUE = 'zomg it is'
SERVER_STATUS_CODE = _types.StatusCode.OK
SERVER_STATUS_DETAILS = 'our work is never over'
REQUEST = 'in death a member of project mayhem has a name'
RESPONSE = 'his name is robert paulson'
METHOD = 'twinkies'
HOST = 'hostess'
server_request_tag = object()
request_call_result = self.server.request_call(self.server_completion_queue, server_request_tag)
self.assertEquals(_types.CallError.OK, request_call_result)
client_call_tag = object()
client_call = self.client_channel.create_call(self.client_completion_queue, METHOD, HOST, DEADLINE)
client_initial_metadata = [(CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE), (CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]
client_start_batch_result = client_call.start_batch([
_types.OpArgs.send_initial_metadata(client_initial_metadata),
_types.OpArgs.send_message(REQUEST),
_types.OpArgs.send_close_from_client(),
_types.OpArgs.recv_initial_metadata(),
_types.OpArgs.recv_message(),
_types.OpArgs.recv_status_on_client()
], client_call_tag)
self.assertEquals(_types.CallError.OK, client_start_batch_result)
request_event = self.server_completion_queue.next(DEADLINE)
self.assertEquals(_types.EventType.OP_COMPLETE, request_event.type)
self.assertIsInstance(request_event.call, _low.Call)
self.assertIs(server_request_tag, request_event.tag)
self.assertEquals(1, len(request_event.results))
self.assertEquals(dict(client_initial_metadata), dict(request_event.results[0].initial_metadata))
self.assertEquals(METHOD, request_event.call_details.method)
self.assertEquals(HOST, request_event.call_details.host)
self.assertLess(abs(DEADLINE - request_event.call_details.deadline), DEADLINE_TOLERANCE)
server_call_tag = object()
server_call = request_event.call
server_initial_metadata = [(SERVER_INITIAL_METADATA_KEY, SERVER_INITIAL_METADATA_VALUE)]
server_trailing_metadata = [(SERVER_TRAILING_METADATA_KEY, SERVER_TRAILING_METADATA_VALUE)]
server_start_batch_result = server_call.start_batch([
_types.OpArgs.send_initial_metadata(server_initial_metadata),
_types.OpArgs.recv_message(),
_types.OpArgs.send_message(RESPONSE),
_types.OpArgs.recv_close_on_server(),
_types.OpArgs.send_status_from_server(server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS)
], server_call_tag)
self.assertEquals(_types.CallError.OK, server_start_batch_result)
client_event = self.client_completion_queue.next(DEADLINE)
server_event = self.server_completion_queue.next(DEADLINE)
self.assertEquals(6, len(client_event.results))
found_client_op_types = set()
for client_result in client_event.results:
self.assertNotIn(client_result.type, found_client_op_types) # we expect each op type to be unique
found_client_op_types.add(client_result.type)
if client_result.type == _types.OpType.RECV_INITIAL_METADATA:
self.assertEquals(dict(server_initial_metadata), dict(client_result.initial_metadata))
elif client_result.type == _types.OpType.RECV_MESSAGE:
self.assertEquals(RESPONSE, client_result.message)
elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT:
self.assertEquals(dict(server_trailing_metadata), dict(client_result.trailing_metadata))
self.assertEquals(SERVER_STATUS_DETAILS, client_result.status.details)
self.assertEquals(SERVER_STATUS_CODE, client_result.status.code)
self.assertEquals(set([
_types.OpType.SEND_INITIAL_METADATA,
_types.OpType.SEND_MESSAGE,
_types.OpType.SEND_CLOSE_FROM_CLIENT,
_types.OpType.RECV_INITIAL_METADATA,
_types.OpType.RECV_MESSAGE,
_types.OpType.RECV_STATUS_ON_CLIENT
]), found_client_op_types)
self.assertEquals(5, len(server_event.results))
found_server_op_types = set()
for server_result in server_event.results:
self.assertNotIn(client_result.type, found_server_op_types)
found_server_op_types.add(server_result.type)
if server_result.type == _types.OpType.RECV_MESSAGE:
self.assertEquals(REQUEST, server_result.message)
elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER:
self.assertFalse(server_result.cancelled)
self.assertEquals(set([
_types.OpType.SEND_INITIAL_METADATA,
_types.OpType.RECV_MESSAGE,
_types.OpType.SEND_MESSAGE,
_types.OpType.RECV_CLOSE_ON_SERVER,
_types.OpType.SEND_STATUS_FROM_SERVER
]), found_server_op_types)
del client_call
del server_call
if __name__ == '__main__':

@ -1,202 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_server.h"
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_call.h"
#include "grpc/_adapter/_completion_queue.h"
#include "grpc/_adapter/_error.h"
#include "grpc/_adapter/_server_credentials.h"
#include "grpc/_adapter/_tag.h"
static int pygrpc_server_init(Server *self, PyObject *args, PyObject *kwds) {
CompletionQueue *completion_queue;
static char *kwlist[] = {"completion_queue", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!:Server", kwlist,
&pygrpc_CompletionQueueType,
&completion_queue)) {
return -1;
}
self->c_server = grpc_server_create(NULL);
grpc_server_register_completion_queue(self->c_server,
completion_queue->c_completion_queue);
self->completion_queue = completion_queue;
Py_INCREF(completion_queue);
return 0;
}
static void pygrpc_server_dealloc(Server *self) {
if (self->c_server != NULL) {
grpc_server_destroy(self->c_server);
}
Py_XDECREF(self->completion_queue);
self->ob_type->tp_free((PyObject *)self);
}
static PyObject *pygrpc_server_add_http2_addr(Server *self, PyObject *args) {
const char *addr;
int port;
if (!PyArg_ParseTuple(args, "s:add_http2_addr", &addr)) {
return NULL;
}
port = grpc_server_add_http2_port(self->c_server, addr);
if (port == 0) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't add port to server!");
return NULL;
}
return PyInt_FromLong(port);
}
static PyObject *pygrpc_server_add_secure_http2_addr(Server *self,
PyObject *args,
PyObject *kwargs) {
const char *addr;
PyObject *server_credentials;
static char *kwlist[] = {"addr", "server_credentials", NULL};
int port;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!:add_secure_http2_addr",
kwlist, &addr, &pygrpc_ServerCredentialsType,
&server_credentials)) {
return NULL;
}
port = grpc_server_add_secure_http2_port(
self->c_server, addr,
((ServerCredentials *)server_credentials)->c_server_credentials);
if (port == 0) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't add port to server!");
return NULL;
}
return PyInt_FromLong(port);
}
static PyObject *pygrpc_server_start(Server *self) {
grpc_server_start(self->c_server);
Py_RETURN_NONE;
}
static const PyObject *pygrpc_server_service(Server *self, PyObject *tag) {
grpc_call_error call_error;
const PyObject *result;
pygrpc_tag *c_tag = pygrpc_tag_new_server_rpc_call(tag);
c_tag->call->completion_queue = self->completion_queue;
c_tag->call->server = self;
Py_INCREF(c_tag->call->completion_queue);
Py_INCREF(c_tag->call->server);
call_error = grpc_server_request_call(
self->c_server, &c_tag->call->c_call, &c_tag->call->call_details,
&c_tag->call->recv_metadata, self->completion_queue->c_completion_queue,
self->completion_queue->c_completion_queue, c_tag);
result = pygrpc_translate_call_error(call_error);
if (result != NULL) {
Py_INCREF(tag);
}
return result;
}
static PyObject *pygrpc_server_stop(Server *self) {
grpc_server_shutdown(self->c_server);
Py_RETURN_NONE;
}
static PyMethodDef methods[] = {
{"add_http2_addr", (PyCFunction)pygrpc_server_add_http2_addr, METH_VARARGS,
"Add an HTTP2 address."},
{"add_secure_http2_addr", (PyCFunction)pygrpc_server_add_secure_http2_addr,
METH_VARARGS, "Add a secure HTTP2 address."},
{"start", (PyCFunction)pygrpc_server_start, METH_NOARGS,
"Starts the server."},
{"service", (PyCFunction)pygrpc_server_service, METH_O, "Services a call."},
{"stop", (PyCFunction)pygrpc_server_stop, METH_NOARGS, "Stops the server."},
{NULL}};
static PyTypeObject pygrpc_ServerType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_gprc.Server", /*tp_name*/
sizeof(Server), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_server_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_server.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pygrpc_server_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
int pygrpc_add_server(PyObject *module) {
if (PyType_Ready(&pygrpc_ServerType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "Server", (PyObject *)&pygrpc_ServerType) ==
-1) {
return -1;
}
return 0;
}

@ -1,152 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_server_credentials.h"
#include <Python.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
static int pygrpc_server_credentials_init(ServerCredentials *self,
PyObject *args, PyObject *kwds) {
char *root_certificates;
PyObject *pair_sequence;
Py_ssize_t pair_count;
grpc_ssl_pem_key_cert_pair *pairs;
int error;
PyObject *iterator;
int i;
PyObject *pair;
static char *kwlist[] = {"root_credentials", "pair_sequence", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "zO:ServerCredentials", kwlist,
&root_certificates, &pair_sequence)) {
return -1;
}
pair_count = PySequence_Length(pair_sequence);
if (pair_count == -1) {
return -1;
}
iterator = PyObject_GetIter(pair_sequence);
if (iterator == NULL) {
return -1;
}
pairs = gpr_malloc(pair_count * sizeof(grpc_ssl_pem_key_cert_pair));
error = 0;
for (i = 0; i < pair_count; i++) {
pair = PyIter_Next(iterator);
if (pair == NULL) {
error = 1;
break;
}
if (!PyArg_ParseTuple(pair, "ss", &pairs[i].private_key,
&pairs[i].cert_chain)) {
error = 1;
Py_DECREF(pair);
break;
}
Py_DECREF(pair);
}
Py_DECREF(iterator);
if (error) {
gpr_free(pairs);
return -1;
} else {
self->c_server_credentials = grpc_ssl_server_credentials_create(
root_certificates, pairs, pair_count);
gpr_free(pairs);
return 0;
}
}
static void pygrpc_server_credentials_dealloc(ServerCredentials *self) {
if (self->c_server_credentials != NULL) {
grpc_server_credentials_release(self->c_server_credentials);
}
self->ob_type->tp_free((PyObject *)self);
}
PyTypeObject pygrpc_ServerCredentialsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_grpc.ServerCredencials", /*tp_name*/
sizeof(ServerCredentials), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)pygrpc_server_credentials_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Wrapping of grpc_server_credentials.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pygrpc_server_credentials_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
int pygrpc_add_server_credentials(PyObject *module) {
if (PyType_Ready(&pygrpc_ServerCredentialsType) < 0) {
return -1;
}
if (PyModule_AddObject(module, "ServerCredentials",
(PyObject *)&pygrpc_ServerCredentialsType) == -1) {
return -1;
}
return 0;
}

@ -1,49 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__SERVER_CREDENTIALS_H_
#define _ADAPTER__SERVER_CREDENTIALS_H_
#include <Python.h>
#include <grpc/grpc_security.h>
typedef struct {
PyObject_HEAD
grpc_server_credentials *c_server_credentials;
} ServerCredentials;
extern PyTypeObject pygrpc_ServerCredentialsType;
int pygrpc_add_server_credentials(PyObject *module);
#endif /* _ADAPTER__SERVER_CREDENTIALS_H_ */

@ -1,65 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "grpc/_adapter/_tag.h"
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
pygrpc_tag *pygrpc_tag_new(pygrpc_tag_type type, PyObject *user_tag,
Call *call) {
pygrpc_tag *self = (pygrpc_tag *)gpr_malloc(sizeof(pygrpc_tag));
memset(self, 0, sizeof(pygrpc_tag));
if (user_tag == NULL) {
self->user_tag = Py_None;
} else {
self->user_tag = user_tag;
}
Py_INCREF(self->user_tag);
self->type = type;
self->call = call;
Py_INCREF(call);
return self;
}
pygrpc_tag *pygrpc_tag_new_server_rpc_call(PyObject *user_tag) {
return pygrpc_tag_new(PYGRPC_SERVER_RPC_NEW, user_tag,
(Call *)pygrpc_CallType.tp_alloc(&pygrpc_CallType, 0));
}
void pygrpc_tag_destroy(pygrpc_tag *self) {
Py_XDECREF(self->user_tag);
Py_XDECREF(self->call);
gpr_free(self);
}

@ -1,70 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ADAPTER__TAG_H_
#define _ADAPTER__TAG_H_
#include <Python.h>
#include <grpc/grpc.h>
#include "grpc/_adapter/_call.h"
#include "grpc/_adapter/_completion_queue.h"
/* grpc_completion_type is becoming meaningless in grpc_event; this is a partial
replacement for its descriptive functionality until Python can move its whole
C and C adapter stack to more closely resemble the core batching API. */
typedef enum {
PYGRPC_SERVER_RPC_NEW = 0,
PYGRPC_INITIAL_METADATA = 1,
PYGRPC_READ = 2,
PYGRPC_WRITE_ACCEPTED = 3,
PYGRPC_FINISH_ACCEPTED = 4,
PYGRPC_CLIENT_METADATA_READ = 5,
PYGRPC_FINISHED_CLIENT = 6,
PYGRPC_FINISHED_SERVER = 7
} pygrpc_tag_type;
typedef struct {
pygrpc_tag_type type;
PyObject *user_tag;
Call *call;
} pygrpc_tag;
pygrpc_tag *pygrpc_tag_new(pygrpc_tag_type type, PyObject *user_tag,
Call *call);
pygrpc_tag *pygrpc_tag_new_server_rpc_call(PyObject *user_tag);
void pygrpc_tag_destroy(pygrpc_tag *self);
#endif /* _ADAPTER__TAG_H_ */

@ -0,0 +1,368 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import abc
import collections
import enum
# TODO(atash): decide whether or not to move these enums to the _c module to
# force build errors with upstream changes.
class GrpcChannelArgumentKeys(enum.Enum):
"""Mirrors keys used in grpc_channel_args for GRPC-specific arguments."""
SSL_TARGET_NAME_OVERRIDE = 'grpc.ssl_target_name_override'
@enum.unique
class CallError(enum.IntEnum):
"""Mirrors grpc_call_error in the C core."""
OK = 0
ERROR = 1
ERROR_NOT_ON_SERVER = 2
ERROR_NOT_ON_CLIENT = 3
ERROR_ALREADY_ACCEPTED = 4
ERROR_ALREADY_INVOKED = 5
ERROR_NOT_INVOKED = 6
ERROR_ALREADY_FINISHED = 7
ERROR_TOO_MANY_OPERATIONS = 8
ERROR_INVALID_FLAGS = 9
ERROR_INVALID_METADATA = 10
@enum.unique
class StatusCode(enum.IntEnum):
"""Mirrors grpc_status_code in the C core."""
OK = 0
CANCELLED = 1
UNKNOWN = 2
INVALID_ARGUMENT = 3
DEADLINE_EXCEEDED = 4
NOT_FOUND = 5
ALREADY_EXISTS = 6
PERMISSION_DENIED = 7
RESOURCE_EXHAUSTED = 8
FAILED_PRECONDITION = 9
ABORTED = 10
OUT_OF_RANGE = 11
UNIMPLEMENTED = 12
INTERNAL = 13
UNAVAILABLE = 14
DATA_LOSS = 15
UNAUTHENTICATED = 16
@enum.unique
class OpType(enum.IntEnum):
"""Mirrors grpc_op_type in the C core."""
SEND_INITIAL_METADATA = 0
SEND_MESSAGE = 1
SEND_CLOSE_FROM_CLIENT = 2
SEND_STATUS_FROM_SERVER = 3
RECV_INITIAL_METADATA = 4
RECV_MESSAGE = 5
RECV_STATUS_ON_CLIENT = 6
RECV_CLOSE_ON_SERVER = 7
@enum.unique
class EventType(enum.IntEnum):
"""Mirrors grpc_completion_type in the C core."""
QUEUE_SHUTDOWN = 0
QUEUE_TIMEOUT = 1 # if seen on the Python side, something went horridly wrong
OP_COMPLETE = 2
class Status(collections.namedtuple(
'Status', [
'code',
'details',
])):
"""The end status of a GRPC call.
Attributes:
code (StatusCode): ...
details (str): ...
"""
class CallDetails(collections.namedtuple(
'CallDetails', [
'method',
'host',
'deadline',
])):
"""Provides information to the server about the client's call.
Attributes:
method (str): ...
host (str): ...
deadline (float): ...
"""
class OpArgs(collections.namedtuple(
'OpArgs', [
'type',
'initial_metadata',
'trailing_metadata',
'message',
'status',
])):
"""Arguments passed into a GRPC operation.
Attributes:
type (OpType): ...
initial_metadata (sequence of 2-sequence of str): Only valid if type ==
OpType.SEND_INITIAL_METADATA, else is None.
trailing_metadata (sequence of 2-sequence of str): Only valid if type ==
OpType.SEND_STATUS_FROM_SERVER, else is None.
message (bytes): Only valid if type == OpType.SEND_MESSAGE, else is None.
status (Status): Only valid if type == OpType.SEND_STATUS_FROM_SERVER, else
is None.
"""
@staticmethod
def send_initial_metadata(initial_metadata):
return OpArgs(OpType.SEND_INITIAL_METADATA, initial_metadata, None, None, None)
@staticmethod
def send_message(message):
return OpArgs(OpType.SEND_MESSAGE, None, None, message, None)
@staticmethod
def send_close_from_client():
return OpArgs(OpType.SEND_CLOSE_FROM_CLIENT, None, None, None, None)
@staticmethod
def send_status_from_server(trailing_metadata, status_code, status_details):
return OpArgs(OpType.SEND_STATUS_FROM_SERVER, None, trailing_metadata, None, Status(status_code, status_details))
@staticmethod
def recv_initial_metadata():
return OpArgs(OpType.RECV_INITIAL_METADATA, None, None, None, None);
@staticmethod
def recv_message():
return OpArgs(OpType.RECV_MESSAGE, None, None, None, None)
@staticmethod
def recv_status_on_client():
return OpArgs(OpType.RECV_STATUS_ON_CLIENT, None, None, None, None)
@staticmethod
def recv_close_on_server():
return OpArgs(OpType.RECV_CLOSE_ON_SERVER, None, None, None, None)
class OpResult(collections.namedtuple(
'OpResult', [
'type',
'initial_metadata',
'trailing_metadata',
'message',
'status',
'cancelled',
])):
"""Results received from a GRPC operation.
Attributes:
type (OpType): ...
initial_metadata (sequence of 2-sequence of str): Only valid if type ==
OpType.RECV_INITIAL_METADATA, else is None.
trailing_metadata (sequence of 2-sequence of str): Only valid if type ==
OpType.RECV_STATUS_ON_CLIENT, else is None.
message (bytes): Only valid if type == OpType.RECV_MESSAGE, else is None.
status (Status): Only valid if type == OpType.RECV_STATUS_ON_CLIENT, else
is None.
cancelled (bool): Only valid if type == OpType.RECV_CLOSE_ON_SERVER, else
is None.
"""
class Event(collections.namedtuple(
'Event', [
'type',
'tag',
'call',
'call_details',
'results',
'success',
])):
"""An event received from a GRPC completion queue.
Attributes:
type (EventType): ...
tag (object): ...
call (Call): The Call object associated with this event (if there is one,
else None).
call_details (CallDetails): The call details associated with the
server-side call (if there is such information, else None).
results (list of OpResult): ...
success (bool): ...
"""
class CompletionQueue:
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __init__(self):
pass
def __iter__(self):
"""This class may be iterated over.
This is the equivalent of calling next() repeatedly with an absolute
deadline of None (i.e. no deadline).
"""
return self
@abc.abstractmethod
def next(self, deadline=float('+inf')):
"""Get the next event on this completion queue.
Args:
deadline (float): absolute deadline in seconds from the Python epoch, or
None for no deadline.
Returns:
Event: ...
"""
pass
@abc.abstractmethod
def shutdown(self):
"""Begin the shutdown process of this completion queue.
Note that this does not immediately destroy the completion queue.
Nevertheless, user code should not pass it around after invoking this.
"""
return None
class Call:
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def start_batch(self, ops, tag):
"""Start a batch of operations.
Args:
ops (sequence of OpArgs): ...
tag (object): ...
Returns:
CallError: ...
"""
return CallError.ERROR
@abc.abstractmethod
def cancel(self, code=None, details=None):
"""Cancel the call.
Args:
code (int): Status code to cancel with (on the server side). If
specified, so must `details`.
details (str): Status details to cancel with (on the server side). If
specified, so must `code`.
Returns:
CallError: ...
"""
return CallError.ERROR
class Channel:
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __init__(self, target, args, credentials=None):
"""Initialize a Channel.
Args:
target (str): ...
args (sequence of 2-sequence of str, (str|integer)): ...
credentials (ClientCredentials): If None, create an insecure channel,
else create a secure channel using the client credentials.
"""
@abc.abstractmethod
def create_call(self, completion_queue, method, host, deadline=float('+inf')):
"""Create a call from this channel.
Args:
completion_queue (CompletionQueue): ...
method (str): ...
host (str): ...
deadline (float): absolute deadline in seconds from the Python epoch, or
None for no deadline.
Returns:
Call: call object associated with this Channel and passed parameters.
"""
return None
class Server:
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def __init__(self, completion_queue, args):
"""Initialize a server.
Args:
completion_queue (CompletionQueue): ...
args (sequence of 2-sequence of str, (str|integer)): ...
"""
@abc.abstractmethod
def add_http2_port(self, address, credentials=None):
"""Adds an HTTP/2 address+port to the server.
Args:
address (str): ...
credentials (ServerCredentials): If None, create an insecure port, else
create a secure port using the server credentials.
"""
@abc.abstractmethod
def start(self):
"""Starts the server."""
@abc.abstractmethod
def shutdown(self, tag=None):
"""Shuts down the server. Does not immediately destroy the server.
Args:
tag (object): if not None, have the server place an event on its
completion queue notifying it when this server has completely shut down.
"""
@abc.abstractmethod
def request_call(self, completion_queue, tag):
"""Requests a call from the server on the server's completion queue.
Args:
completion_queue (CompletionQueue): Completion queue for the call. May be
the same as the server's completion queue.
tag (object) ...
"""

@ -35,7 +35,7 @@ import threading
import time
from grpc._adapter import _common
from grpc._adapter import _low
from grpc._adapter import _intermediary_low as _low
from grpc.framework.base import interfaces as base_interfaces
from grpc.framework.base import null
from grpc.framework.foundation import activated
@ -204,7 +204,7 @@ class ForeLink(base_interfaces.ForeLink, activated.Activated):
call, sequence_number,
base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None,
None, None, None)
elif code is _low.Code.EXPIRED:
elif code is _low.Code.DEADLINE_EXCEEDED:
ticket = base_interfaces.FrontToBackTicket(
call, sequence_number,
base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None,

@ -35,7 +35,7 @@ import threading
import time
from grpc._adapter import _common
from grpc._adapter import _low
from grpc._adapter import _intermediary_low as _low
from grpc.framework.base import interfaces as base_interfaces
from grpc.framework.base import null
from grpc.framework.foundation import activated
@ -195,7 +195,7 @@ class RearLink(base_interfaces.RearLink, activated.Activated):
kind = base_interfaces.BackToFrontTicket.Kind.COMPLETION
elif event.status.code is _low.Code.CANCELLED:
kind = base_interfaces.BackToFrontTicket.Kind.CANCELLATION
elif event.status.code is _low.Code.EXPIRED:
elif event.status.code is _low.Code.DEADLINE_EXCEEDED:
kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION
else:
kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE

@ -34,15 +34,15 @@ import setuptools
import sys
_EXTENSION_SOURCES = (
'grpc/_adapter/_c.c',
'grpc/_adapter/_call.c',
'grpc/_adapter/_channel.c',
'grpc/_adapter/_completion_queue.c',
'grpc/_adapter/_error.c',
'grpc/_adapter/_server.c',
'grpc/_adapter/_client_credentials.c',
'grpc/_adapter/_server_credentials.c',
'grpc/_adapter/_tag.c'
'grpc/_adapter/_c/module.c',
'grpc/_adapter/_c/types.c',
'grpc/_adapter/_c/utility.c',
'grpc/_adapter/_c/types/client_credentials.c',
'grpc/_adapter/_c/types/server_credentials.c',
'grpc/_adapter/_c/types/completion_queue.c',
'grpc/_adapter/_c/types/call.c',
'grpc/_adapter/_c/types/channel.c',
'grpc/_adapter/_c/types/server.c',
)
_EXTENSION_INCLUDE_DIRECTORIES = (

@ -1,27 +1,27 @@
[
{
"file": "test/compiler/python_plugin_test.py"
"module": "grpc._adapter._c_test"
},
{
"module": "grpc._adapter._blocking_invocation_inline_service_test"
"module": "grpc._adapter._low_test"
},
{
"module": "grpc._adapter._c_test"
"module": "grpc._adapter._intermediary_low_test"
},
{
"module": "grpc._adapter._event_invocation_synchronous_event_service_test"
"module": "grpc._adapter._links_test"
},
{
"module": "grpc._adapter._future_invocation_asynchronous_event_service_test"
"module": "grpc._adapter._lonely_rear_link_test"
},
{
"module": "grpc._adapter._links_test"
"module": "grpc._adapter._blocking_invocation_inline_service_test"
},
{
"module": "grpc._adapter._lonely_rear_link_test"
"module": "grpc._adapter._event_invocation_synchronous_event_service_test"
},
{
"module": "grpc._adapter._low_test"
"module": "grpc._adapter._future_invocation_asynchronous_event_service_test"
},
{
"module": "grpc.early_adopter.implementations_test"
@ -49,5 +49,8 @@
},
{
"module": "interop._secure_interop_test"
},
{
"file": "test/compiler/python_plugin_test.py"
}
]

Loading…
Cancel
Save