From 4a4f1496c1b2fe6dc8336ee5f00c09568165a42a Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 21 Jul 2015 16:32:29 -0700 Subject: Rename grpc_channel_create to grpc_insecure_channel_create --- src/python/src/grpc/_adapter/_c/types/channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/src/grpc/_adapter/_c/types/channel.c b/src/python/src/grpc/_adapter/_c/types/channel.c index c235597466..feb256cf00 100644 --- a/src/python/src/grpc/_adapter/_c/types/channel.c +++ b/src/python/src/grpc/_adapter/_c/types/channel.c @@ -104,7 +104,7 @@ Channel *pygrpc_Channel_new( 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); + self->c_chan = grpc_insecure_channel_create(target, &c_args); } pygrpc_discard_channel_args(c_args); return self; -- cgit v1.2.3 From 5029b30d1ce8509a4b2f7c17e88bddb8a5206f56 Mon Sep 17 00:00:00 2001 From: Julien Boeuf Date: Tue, 21 Jul 2015 23:02:16 -0700 Subject: Adding option to force client auth on the server SSL creds. --- include/grpc++/server_credentials.h | 1 + include/grpc/grpc_security.h | 9 ++++++--- src/core/security/credentials.c | 8 +++++--- src/core/security/security_connector.c | 7 ++++--- src/core/security/security_connector.h | 1 + src/core/tsi/ssl_transport_security.c | 8 +++++--- src/core/tsi/ssl_transport_security.h | 16 ++++++++++------ src/cpp/server/secure_server_credentials.cc | 3 ++- src/csharp/ext/grpc_csharp_ext.c | 3 ++- src/node/ext/server_credentials.cc | 4 +++- src/php/ext/grpc/server_credentials.c | 6 ++++-- .../src/grpc/_adapter/_c/types/server_credentials.c | 4 +++- src/ruby/ext/grpc/rb_server_credentials.c | 5 +++-- test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c | 2 +- .../fixtures/chttp2_simple_ssl_fullstack_with_poll.c | 2 +- .../fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c | 2 +- test/core/fling/server.c | 2 +- 17 files changed, 53 insertions(+), 30 deletions(-) (limited to 'src/python') diff --git a/include/grpc++/server_credentials.h b/include/grpc++/server_credentials.h index 83ae9fd1eb..94041c7757 100644 --- a/include/grpc++/server_credentials.h +++ b/include/grpc++/server_credentials.h @@ -64,6 +64,7 @@ struct SslServerCredentialsOptions { }; grpc::string pem_root_certs; std::vector pem_key_cert_pairs; + bool force_client_auth; }; // Builds SSL ServerCredentials given SSL specific options diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 37d66c04ae..7c5bd6433f 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -87,7 +87,7 @@ typedef struct { directory). - pem_key_cert_pair is a pointer on the object containing client's private key and certificate chain. This parameter can be NULL if the client does - not have such a key/cert pair. */ + not have such a key/cert pair. */ grpc_credentials *grpc_ssl_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair); @@ -177,10 +177,13 @@ void grpc_server_credentials_release(grpc_server_credentials *creds); - pem_key_cert_pairs is an array private key / certificate chains of the server. This parameter cannot be NULL. - num_key_cert_pairs indicates the number of items in the private_key_files - and cert_chain_files parameters. It should be at least 1. */ + and cert_chain_files parameters. It should be at least 1. + - force_client_auth, if set to non-zero will force the client to authenticate + with an SSL cert. Note that this option is ignored if pem_root_certs is + NULL. */ grpc_server_credentials *grpc_ssl_server_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs); + size_t num_key_cert_pairs, int force_client_auth); /* Creates a fake server transport security credentials object for testing. */ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index fb59fa4b0e..a4d998a429 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -259,8 +259,10 @@ static void ssl_build_config(const char *pem_root_certs, static void ssl_build_server_config( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, grpc_ssl_server_config *config) { + size_t num_key_cert_pairs, int force_client_auth, + grpc_ssl_server_config *config) { size_t i; + config->force_client_auth = force_client_auth; if (pem_root_certs != NULL) { ssl_copy_key_material(pem_root_certs, &config->pem_root_certs, &config->pem_root_certs_size); @@ -302,14 +304,14 @@ grpc_credentials *grpc_ssl_credentials_create( grpc_server_credentials *grpc_ssl_server_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs) { + size_t num_key_cert_pairs, int force_client_auth) { grpc_ssl_server_credentials *c = gpr_malloc(sizeof(grpc_ssl_server_credentials)); memset(c, 0, sizeof(grpc_ssl_server_credentials)); c->base.type = GRPC_CREDENTIALS_TYPE_SSL; c->base.vtable = &ssl_server_vtable; ssl_build_server_config(pem_root_certs, pem_key_cert_pairs, - num_key_cert_pairs, &c->config); + num_key_cert_pairs, force_client_auth, &c->config); return &c->base; } diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index f6e423eb27..726b4c1e12 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -653,9 +653,10 @@ grpc_security_status grpc_ssl_server_security_connector_create( config->pem_private_keys_sizes, (const unsigned char **)config->pem_cert_chains, config->pem_cert_chains_sizes, config->num_key_cert_pairs, - config->pem_root_certs, config->pem_root_certs_size, ssl_cipher_suites(), - alpn_protocol_strings, alpn_protocol_string_lengths, - (uint16_t)num_alpn_protocols, &c->handshaker_factory); + config->pem_root_certs, config->pem_root_certs_size, + config->force_client_auth, ssl_cipher_suites(), alpn_protocol_strings, + alpn_protocol_string_lengths, (uint16_t)num_alpn_protocols, + &c->handshaker_factory); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", tsi_result_to_string(result)); diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h index a4c723f026..2c9aa1c5a4 100644 --- a/src/core/security/security_connector.h +++ b/src/core/security/security_connector.h @@ -201,6 +201,7 @@ typedef struct { size_t num_key_cert_pairs; unsigned char *pem_root_certs; size_t pem_root_certs_size; + int force_client_auth; } grpc_ssl_server_config; /* Creates an SSL server_security_connector. diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c index 6156a39d09..609fc06ed5 100644 --- a/src/core/tsi/ssl_transport_security.c +++ b/src/core/tsi/ssl_transport_security.c @@ -1293,8 +1293,8 @@ tsi_result tsi_create_ssl_server_handshaker_factory( const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, const unsigned char* pem_client_root_certs, - size_t pem_client_root_certs_size, const char* cipher_list, - const unsigned char** alpn_protocols, + size_t pem_client_root_certs_size, int force_client_auth, + const char* cipher_list, const unsigned char** alpn_protocols, const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, tsi_ssl_handshaker_factory** factory) { tsi_ssl_server_handshaker_factory* impl = NULL; @@ -1349,6 +1349,7 @@ tsi_result tsi_create_ssl_server_handshaker_factory( if (result != TSI_OK) break; if (pem_client_root_certs != NULL) { + int flags = SSL_VERIFY_PEER; STACK_OF(X509_NAME)* root_names = NULL; result = ssl_ctx_load_verification_certs( impl->ssl_contexts[i], pem_client_root_certs, @@ -1358,7 +1359,8 @@ tsi_result tsi_create_ssl_server_handshaker_factory( break; } SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); - SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, NULL); + if (force_client_auth) flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + SSL_CTX_set_verify(impl->ssl_contexts[i], flags, NULL); /* TODO(jboeuf): Add revocation verification. */ } diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index b2aa2f393e..4bf6c81b75 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -107,10 +107,14 @@ tsi_result tsi_create_ssl_client_handshaker_factory( - key_cert_pair_count indicates the number of items in the private_key_files and cert_chain_files parameters. - pem_client_roots is the buffer containing the PEM encoding of the client - root certificates. This parameter may be NULL in which case the server - will not ask the client to authenticate itself with a certificate (server- - only authentication mode). - - pem_client_roots_size is the size of the associated buffer. + root certificates. This parameter may be NULL in which case the server will + not authenticate the client. If not NULL, the force_client_auth parameter + specifies if the server will accept only authenticated clients or both + authenticated and non-authenticated clients. + - pem_client_root_certs_size is the size of the associated buffer. + - force_client_auth, if set to non-zero will force the client to authenticate + with an SSL cert. Note that this option is ignored if pem_client_root_certs + is NULL or pem_client_roots_certs_size is 0 - cipher_suites contains an optional list of the ciphers that the server supports. The format of this string is described in: https://www.openssl.org/docs/apps/ciphers.html. @@ -131,8 +135,8 @@ tsi_result tsi_create_ssl_server_handshaker_factory( const size_t* pem_private_keys_sizes, const unsigned char** pem_cert_chains, const size_t* pem_cert_chains_sizes, size_t key_cert_pair_count, const unsigned char* pem_client_root_certs, - size_t pem_client_root_certs_size, const char* cipher_suites, - const unsigned char** alpn_protocols, + size_t pem_client_root_certs_size, int force_client_auth, + const char* cipher_suites, const unsigned char** alpn_protocols, const unsigned char* alpn_protocols_lengths, uint16_t num_alpn_protocols, tsi_ssl_handshaker_factory** factory); diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc index 3e262dd74f..32c45e2280 100644 --- a/src/cpp/server/secure_server_credentials.cc +++ b/src/cpp/server/secure_server_credentials.cc @@ -51,7 +51,8 @@ std::shared_ptr SslServerCredentials( } grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create( options.pem_root_certs.empty() ? nullptr : options.pem_root_certs.c_str(), - &pem_key_cert_pairs[0], pem_key_cert_pairs.size()); + &pem_key_cert_pairs[0], pem_key_cert_pairs.size(), + options.force_client_auth); return std::shared_ptr( new SecureServerCredentials(c_creds)); } diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 7dd1959a5f..3c0035d453 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -718,8 +718,9 @@ grpcsharp_ssl_server_credentials_create( key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i]; } } + /* TODO: Add a force_client_auth parameter and pass it here. */ creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs, - num_key_cert_pairs); + num_key_cert_pairs, 0); gpr_free(key_cert_pairs); return creds; } diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc index d2b63cdc4e..709105c72f 100644 --- a/src/node/ext/server_credentials.cc +++ b/src/node/ext/server_credentials.cc @@ -140,8 +140,10 @@ NAN_METHOD(ServerCredentials::CreateSsl) { return NanThrowTypeError("createSsl's third argument must be a Buffer"); } key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]); + // TODO Add a force_client_auth parameter and pass it as the last parameter + // here. NanReturnValue(WrapStruct( - grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1))); + grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1, 0))); } NAN_METHOD(ServerCredentials::CreateFake) { diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index c4c1fabb1a..ec1c3c4f52 100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -115,8 +115,10 @@ PHP_METHOD(ServerCredentials, createSsl) { "createSsl expects 3 strings", 1 TSRMLS_CC); return; } - grpc_server_credentials *creds = - grpc_ssl_server_credentials_create(pem_root_certs, &pem_key_cert_pair, 1); + /* TODO: add a force_client_auth field in ServerCredentials and pass it as + * the last parameter. */ + grpc_server_credentials *creds = grpc_ssl_server_credentials_create( + pem_root_certs, &pem_key_cert_pair, 1, 0); zval *creds_object = grpc_php_wrap_server_credentials(creds); RETURN_DESTROY_ZVAL(creds_object); } diff --git a/src/python/src/grpc/_adapter/_c/types/server_credentials.c b/src/python/src/grpc/_adapter/_c/types/server_credentials.c index 2e02c8fe81..2277b5b907 100644 --- a/src/python/src/grpc/_adapter/_c/types/server_credentials.c +++ b/src/python/src/grpc/_adapter/_c/types/server_credentials.c @@ -131,8 +131,10 @@ ServerCredentials *pygrpc_ServerCredentials_ssl( } self = (ServerCredentials *)type->tp_alloc(type, 0); + /* TODO: Add a force_client_auth parameter in the python object and pass it + here as the last arg. */ self->c_creds = grpc_ssl_server_credentials_create( - root_certs, key_cert_pairs, num_key_cert_pairs); + root_certs, key_cert_pairs, num_key_cert_pairs, 0); gpr_free(key_cert_pairs); return self; } diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c index 5f40935890..62c211d769 100644 --- a/src/ruby/ext/grpc/rb_server_credentials.c +++ b/src/ruby/ext/grpc/rb_server_credentials.c @@ -176,11 +176,12 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs, } key_cert_pair.private_key = RSTRING_PTR(pem_private_key); key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain); + /* TODO Add a force_client_auth parameter and pass it here. */ if (pem_root_certs == Qnil) { - creds = grpc_ssl_server_credentials_create(NULL, &key_cert_pair, 1); + creds = grpc_ssl_server_credentials_create(NULL, &key_cert_pair, 1, 0); } else { creds = grpc_ssl_server_credentials_create(RSTRING_PTR(pem_root_certs), - &key_cert_pair, 1); + &key_cert_pair, 1, 0); } if (creds == NULL) { rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c index 73a36116fb..6d5669d05a 100644 --- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c +++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c @@ -115,7 +115,7 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, test_server1_cert}; grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1); + grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0); chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); } diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c index b1ac3e535f..d0cc3dd74a 100644 --- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c +++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c @@ -115,7 +115,7 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, test_server1_cert}; grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1); + grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0); chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); } diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c index de418bf7ee..f74ed9365f 100644 --- a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c +++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c @@ -120,7 +120,7 @@ static void chttp2_init_server_simple_ssl_secure_fullstack( grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key, test_server1_cert}; grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1); + grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1, 0); chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); } diff --git a/test/core/fling/server.c b/test/core/fling/server.c index 082bbd368a..8f349044d9 100644 --- a/test/core/fling/server.c +++ b/test/core/fling/server.c @@ -210,7 +210,7 @@ int main(int argc, char **argv) { grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key, test_server1_cert}; grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1); + grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1, 0); server = grpc_server_create(NULL); GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr, ssl_creds)); grpc_server_credentials_release(ssl_creds); -- cgit v1.2.3 From 8b25f2aaebc261004a08fb05151795f0c332b066 Mon Sep 17 00:00:00 2001 From: yang-g Date: Tue, 21 Jul 2015 23:54:36 -0700 Subject: move fake_transport_security_credentials to private API --- Makefile | 2 - build.json | 2 - include/grpc/grpc_security.h | 8 --- src/core/security/credentials.h | 8 +++ src/node/ext/credentials.cc | 7 --- src/php/ext/grpc/credentials.c | 10 ---- .../grpc/_adapter/_c/types/client_credentials.c | 17 ------ .../src/grpc/_cython/_cygrpc/credentials.pyx | 11 ---- src/python/src/grpc/_cython/_cygrpc/grpc.pxd | 2 - test/cpp/end2end/end2end_test.cc | 60 +++++++++++++--------- test/cpp/util/fake_credentials.cc | 58 --------------------- test/cpp/util/fake_credentials.h | 51 ------------------ tools/run_tests/sources_and_headers.json | 3 -- 13 files changed, 44 insertions(+), 195 deletions(-) delete mode 100644 test/cpp/util/fake_credentials.cc delete mode 100644 test/cpp/util/fake_credentials.h (limited to 'src/python') diff --git a/Makefile b/Makefile index 2ae0cd052d..b5fc934806 100644 --- a/Makefile +++ b/Makefile @@ -4117,7 +4117,6 @@ LIBGRPC++_TEST_UTIL_SRC = \ $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc \ test/cpp/util/cli_call.cc \ test/cpp/util/create_test_channel.cc \ - test/cpp/util/fake_credentials.cc \ test/cpp/util/subprocess.cc \ @@ -4164,7 +4163,6 @@ endif endif $(OBJDIR)/$(CONFIG)/test/cpp/util/cli_call.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/create_test_channel.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc -$(OBJDIR)/$(CONFIG)/test/cpp/util/fake_credentials.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc $(OBJDIR)/$(CONFIG)/test/cpp/util/subprocess.o: $(GENDIR)/test/cpp/util/messages.pb.cc $(GENDIR)/test/cpp/util/messages.grpc.pb.cc $(GENDIR)/test/cpp/util/echo.pb.cc $(GENDIR)/test/cpp/util/echo.grpc.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.pb.cc $(GENDIR)/test/cpp/util/echo_duplicate.grpc.pb.cc diff --git a/build.json b/build.json index 2755703e1c..e61dc67a68 100644 --- a/build.json +++ b/build.json @@ -609,7 +609,6 @@ "headers": [ "test/cpp/util/cli_call.h", "test/cpp/util/create_test_channel.h", - "test/cpp/util/fake_credentials.h", "test/cpp/util/subprocess.h" ], "src": [ @@ -618,7 +617,6 @@ "test/cpp/util/echo_duplicate.proto", "test/cpp/util/cli_call.cc", "test/cpp/util/create_test_channel.cc", - "test/cpp/util/fake_credentials.cc", "test/cpp/util/subprocess.cc" ], "deps": [ diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 37d66c04ae..18d9d2f899 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -140,9 +140,6 @@ grpc_credentials *grpc_access_token_credentials_create( grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, const char *authority_selector); -/* Creates a fake transport security credentials object for testing. */ -grpc_credentials *grpc_fake_transport_security_credentials_create(void); - /* --- Secure channel creation. --- */ /* The caller of the secure_channel_create functions may override the target @@ -182,10 +179,6 @@ grpc_server_credentials *grpc_ssl_server_credentials_create( const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs); -/* Creates a fake server transport security credentials object for testing. */ -grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( - void); - /* --- Server-side secure ports. --- */ /* Add a HTTP2 over an encrypted link over tcp listener. @@ -206,7 +199,6 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call, /* TODO(jboeuf): Define some well-known property names. */ #define GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME "transport_security_type" -#define GRPC_FAKE_TRANSPORT_SECURITY_TYPE "fake" #define GRPC_SSL_TRANSPORT_SECURITY_TYPE "ssl" #define GRPC_X509_CN_PROPERTY_NAME "x509_common_name" diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h index d988901cf7..f5635122af 100644 --- a/src/core/security/credentials.h +++ b/src/core/security/credentials.h @@ -52,6 +52,8 @@ typedef enum { GRPC_CREDENTIALS_ERROR } grpc_credentials_status; +#define GRPC_FAKE_TRANSPORT_SECURITY_TYPE "fake" + #define GRPC_CREDENTIALS_TYPE_SSL "Ssl" #define GRPC_CREDENTIALS_TYPE_OAUTH2 "Oauth2" #define GRPC_CREDENTIALS_TYPE_JWT "Jwt" @@ -112,6 +114,12 @@ void grpc_credentials_md_store_unref(grpc_credentials_md_store *store); /* --- grpc_credentials. --- */ +/* Creates a fake transport security credentials object for testing. */ +grpc_credentials *grpc_fake_transport_security_credentials_create(void); +/* Creates a fake server transport security credentials object for testing. */ +grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( + void); + /* It is the caller's responsibility to gpr_free the result if not NULL. */ char *grpc_get_well_known_google_credentials_file_path(void); diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc index 34872017ea..d6cff0631d 100644 --- a/src/node/ext/credentials.cc +++ b/src/node/ext/credentials.cc @@ -79,8 +79,6 @@ void Credentials::Init(Handle exports) { NanNew(CreateComposite)->GetFunction()); ctr->Set(NanNew("createGce"), NanNew(CreateGce)->GetFunction()); - ctr->Set(NanNew("createFake"), - NanNew(CreateFake)->GetFunction()); ctr->Set(NanNew("createIam"), NanNew(CreateIam)->GetFunction()); constructor = new NanCallback(ctr); @@ -180,11 +178,6 @@ NAN_METHOD(Credentials::CreateGce) { NanReturnValue(WrapStruct(grpc_compute_engine_credentials_create())); } -NAN_METHOD(Credentials::CreateFake) { - NanScope(); - NanReturnValue(WrapStruct(grpc_fake_transport_security_credentials_create())); -} - NAN_METHOD(Credentials::CreateIam) { NanScope(); if (!args[0]->IsString()) { diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index a262b9981f..3e781d4747 100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -175,15 +175,6 @@ PHP_METHOD(Credentials, createGce) { RETURN_DESTROY_ZVAL(creds_object); } -/** - * Create fake credentials. Only to be used for testing. - * @return Credentials The new fake credentials object - */ -PHP_METHOD(Credentials, createFake) { - grpc_credentials *creds = grpc_fake_transport_security_credentials_create(); - zval *creds_object = grpc_php_wrap_credentials(creds); - RETURN_DESTROY_ZVAL(creds_object); -} static zend_function_entry credentials_methods[] = { PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -191,7 +182,6 @@ static zend_function_entry credentials_methods[] = { PHP_ME(Credentials, createComposite, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(Credentials, createGce, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Credentials, createFake, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_credentials(TSRMLS_D) { diff --git a/src/python/src/grpc/_adapter/_c/types/client_credentials.c b/src/python/src/grpc/_adapter/_c/types/client_credentials.c index 6a4561c060..6e1a3e6f30 100644 --- a/src/python/src/grpc/_adapter/_c/types/client_credentials.c +++ b/src/python/src/grpc/_adapter/_c/types/client_credentials.c @@ -54,9 +54,6 @@ PyMethodDef pygrpc_ClientCredentials_methods[] = { 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} @@ -249,20 +246,6 @@ ClientCredentials *pygrpc_ClientCredentials_refresh_token( 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; diff --git a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx b/src/python/src/grpc/_cython/_cygrpc/credentials.pyx index c14d8844dd..14e7ce84c0 100644 --- a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx +++ b/src/python/src/grpc/_cython/_cygrpc/credentials.pyx @@ -152,12 +152,6 @@ def client_credentials_refresh_token(json_refresh_token): credentials.references.append(json_refresh_token) return credentials -def client_credentials_fake_transport_security(): - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = ( - grpc.grpc_fake_transport_security_credentials_create()) - return credentials - def client_credentials_iam(authorization_token, authority_selector): if isinstance(authorization_token, bytes): pass @@ -210,8 +204,3 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): ) return credentials -def server_credentials_fake_transport_security(): - cdef ServerCredentials credentials = ServerCredentials() - credentials.c_credentials = ( - grpc.grpc_fake_transport_security_server_credentials_create()) - return credentials diff --git a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd b/src/python/src/grpc/_cython/_cygrpc/grpc.pxd index 7db8fbe31c..7f36136250 100644 --- a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd +++ b/src/python/src/grpc/_cython/_cygrpc/grpc.pxd @@ -317,7 +317,6 @@ cdef extern from "grpc/grpc_security.h": gpr_timespec token_lifetime) grpc_credentials *grpc_refresh_token_credentials_create( const char *json_refresh_token) - grpc_credentials *grpc_fake_transport_security_credentials_create() grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, const char *authority_selector) void grpc_credentials_release(grpc_credentials *creds) @@ -334,7 +333,6 @@ cdef extern from "grpc/grpc_security.h": const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, size_t num_key_cert_pairs); - grpc_server_credentials *grpc_fake_transport_security_server_credentials_create() void grpc_server_credentials_release(grpc_server_credentials *creds) int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index 8b4424c735..fc6b54305c 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -35,11 +35,11 @@ #include #include "src/core/security/credentials.h" +#include "test/core/end2end/data/ssl_test_data.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" #include "test/cpp/util/echo_duplicate.grpc.pb.h" #include "test/cpp/util/echo.grpc.pb.h" -#include "test/cpp/util/fake_credentials.h" #include #include #include @@ -83,13 +83,12 @@ void MaybeEchoDeadline(ServerContext* context, const EchoRequest* request, } } -template -void CheckAuthContext(T* context) { +void CheckServerAuthContext(const ServerContext* context) { std::shared_ptr auth_ctx = context->auth_context(); - std::vector fake = + std::vector ssl = auth_ctx->FindPropertyValues("transport_security_type"); - EXPECT_EQ(1u, fake.size()); - EXPECT_EQ("fake", fake[0]); + EXPECT_EQ(1u, ssl.size()); + EXPECT_EQ("ssl", ssl[0]); EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty()); EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty()); } @@ -142,7 +141,7 @@ class TestServiceImpl : public ::grpc::cpp::test::util::TestService::Service { } } if (request->has_param() && request->param().check_auth_context()) { - CheckAuthContext(context); + CheckServerAuthContext(context); } return Status::OK; } @@ -235,10 +234,15 @@ class End2endTest : public ::testing::Test { server_address_ << "localhost:" << port; // Setup server ServerBuilder builder; + SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key, + test_server1_cert}; + SslServerCredentialsOptions ssl_opts; + ssl_opts.pem_root_certs = ""; + ssl_opts.pem_key_cert_pairs.push_back(pkcp); builder.AddListeningPort(server_address_.str(), - FakeTransportSecurityServerCredentials()); + SslServerCredentials(ssl_opts)); builder.RegisterService(&service_); - builder.RegisterService("special", &special_service_); + builder.RegisterService("foo.test.youtube.com", &special_service_); builder.SetMaxMessageSize( kMaxMessageSize_); // For testing max message size. builder.RegisterService(&dup_pkg_service_); @@ -249,12 +253,15 @@ class End2endTest : public ::testing::Test { void TearDown() GRPC_OVERRIDE { server_->Shutdown(); } void ResetStub() { - std::shared_ptr channel = - CreateChannel(server_address_.str(), FakeTransportSecurityCredentials(), - ChannelArguments()); - stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel)); + SslCredentialsOptions ssl_opts = {test_root_cert, "", ""}; + ChannelArguments args; + args.SetSslTargetNameOverride("foo.test.google.fr"); + channel_ = CreateChannel(server_address_.str(), SslCredentials(ssl_opts), + args); + stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel_)); } + std::shared_ptr channel_; std::unique_ptr stub_; std::unique_ptr server_; std::ostringstream server_address_; @@ -288,11 +295,11 @@ TEST_F(End2endTest, SimpleRpcWithHost) { request.set_message("Hello"); ClientContext context; - context.set_authority("special"); + context.set_authority("foo.test.youtube.com"); Status s = stub_->Echo(&context, request, &response); EXPECT_EQ(response.message(), request.message()); EXPECT_TRUE(response.has_param()); - EXPECT_EQ(response.param().host(), "special"); + EXPECT_EQ("special", response.param().host()); EXPECT_TRUE(s.ok()); } @@ -481,24 +488,19 @@ TEST_F(End2endTest, BidiStream) { // Talk to the two services with the same name but different package names. // The two stubs are created on the same channel. TEST_F(End2endTest, DiffPackageServices) { - std::shared_ptr channel = - CreateChannel(server_address_.str(), FakeTransportSecurityCredentials(), - ChannelArguments()); - + ResetStub(); EchoRequest request; EchoResponse response; request.set_message("Hello"); - std::unique_ptr stub( - grpc::cpp::test::util::TestService::NewStub(channel)); ClientContext context; - Status s = stub->Echo(&context, request, &response); + Status s = stub_->Echo(&context, request, &response); EXPECT_EQ(response.message(), request.message()); EXPECT_TRUE(s.ok()); std::unique_ptr dup_pkg_stub( - grpc::cpp::test::util::duplicate::TestService::NewStub(channel)); + grpc::cpp::test::util::duplicate::TestService::NewStub(channel_)); ClientContext context2; s = dup_pkg_stub->Echo(&context2, request, &response); EXPECT_EQ("no package", response.message()); @@ -782,7 +784,17 @@ TEST_F(End2endTest, ClientAuthContext) { EXPECT_EQ(response.message(), request.message()); EXPECT_TRUE(s.ok()); - CheckAuthContext(&context); + std::shared_ptr auth_ctx = context.auth_context(); + std::vector ssl = + auth_ctx->FindPropertyValues("transport_security_type"); + EXPECT_EQ(1u, ssl.size()); + EXPECT_EQ("ssl", ssl[0]); + EXPECT_EQ("x509_subject_alternative_name", + auth_ctx->GetPeerIdentityPropertyName()); + EXPECT_EQ(3u, auth_ctx->GetPeerIdentity().size()); + EXPECT_EQ("*.test.google.fr", auth_ctx->GetPeerIdentity()[0]); + EXPECT_EQ("waterzooi.test.google.be", auth_ctx->GetPeerIdentity()[1]); + EXPECT_EQ("*.test.youtube.com", auth_ctx->GetPeerIdentity()[2]); } } // namespace testing diff --git a/test/cpp/util/fake_credentials.cc b/test/cpp/util/fake_credentials.cc deleted file mode 100644 index f5b83b8159..0000000000 --- a/test/cpp/util/fake_credentials.cc +++ /dev/null @@ -1,58 +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 -#include -#include -#include -#include "src/cpp/client/channel.h" -#include "src/cpp/client/secure_credentials.h" -#include "src/cpp/server/secure_server_credentials.h" - -namespace grpc { -namespace testing { - -std::shared_ptr FakeTransportSecurityCredentials() { - grpc_credentials* c_creds = grpc_fake_transport_security_credentials_create(); - return std::shared_ptr(new SecureCredentials(c_creds)); -} - -std::shared_ptr FakeTransportSecurityServerCredentials() { - grpc_server_credentials* c_creds = - grpc_fake_transport_security_server_credentials_create(); - return std::shared_ptr( - new SecureServerCredentials(c_creds)); -} - -} // namespace testing -} // namespace grpc diff --git a/test/cpp/util/fake_credentials.h b/test/cpp/util/fake_credentials.h deleted file mode 100644 index e1ba7bb9e4..0000000000 --- a/test/cpp/util/fake_credentials.h +++ /dev/null @@ -1,51 +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 GRPC_TEST_CPP_UTIL_FAKE_CREDENTIALS_H -#define GRPC_TEST_CPP_UTIL_FAKE_CREDENTIALS_H - -#include - -namespace grpc { -class Credentials; -class ServerCredentials; - -namespace testing { - -std::shared_ptr FakeTransportSecurityCredentials(); -std::shared_ptr FakeTransportSecurityServerCredentials(); - -} // namespace testing -} // namespace grpc - -#endif // GRPC_TEST_CPP_UTIL_FAKE_CREDENTIALS_H diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index abddaab699..15f6639662 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -10783,7 +10783,6 @@ "test/cpp/util/echo.pb.h", "test/cpp/util/echo_duplicate.grpc.pb.h", "test/cpp/util/echo_duplicate.pb.h", - "test/cpp/util/fake_credentials.h", "test/cpp/util/messages.grpc.pb.h", "test/cpp/util/messages.pb.h", "test/cpp/util/subprocess.h" @@ -10795,8 +10794,6 @@ "test/cpp/util/cli_call.h", "test/cpp/util/create_test_channel.cc", "test/cpp/util/create_test_channel.h", - "test/cpp/util/fake_credentials.cc", - "test/cpp/util/fake_credentials.h", "test/cpp/util/subprocess.cc", "test/cpp/util/subprocess.h" ] -- cgit v1.2.3 From 52e4de1ea1b2c9b07d53b557bef943d507f5d9c9 Mon Sep 17 00:00:00 2001 From: yang-g Date: Wed, 22 Jul 2015 10:33:18 -0700 Subject: Fix node test. Remove all the server fake credentials references --- src/node/ext/server_credentials.cc | 8 -------- src/node/ext/server_credentials.h | 1 - src/node/test/server_test.js | 10 ++++++++-- src/php/ext/grpc/credentials.c | 1 - src/php/ext/grpc/server_credentials.c | 13 ------------- src/python/src/grpc/_adapter/_c/types.h | 4 ---- src/python/src/grpc/_adapter/_c/types/server_credentials.c | 10 ---------- src/python/src/grpc/_adapter/_c_test.py | 12 ------------ src/python/src/grpc/_cython/adapter_low.py | 8 -------- src/python/src/grpc/_cython/cygrpc.pyx | 4 ---- src/python/src/grpc/_cython/cygrpc_test.py | 14 -------------- 11 files changed, 8 insertions(+), 77 deletions(-) (limited to 'src/python') diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc index d2b63cdc4e..66aaa3300f 100644 --- a/src/node/ext/server_credentials.cc +++ b/src/node/ext/server_credentials.cc @@ -73,8 +73,6 @@ void ServerCredentials::Init(Handle exports) { Handle ctr = tpl->GetFunction(); ctr->Set(NanNew("createSsl"), NanNew(CreateSsl)->GetFunction()); - ctr->Set(NanNew("createFake"), - NanNew(CreateFake)->GetFunction()); constructor = new NanCallback(ctr); exports->Set(NanNew("ServerCredentials"), ctr); } @@ -144,11 +142,5 @@ NAN_METHOD(ServerCredentials::CreateSsl) { grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1))); } -NAN_METHOD(ServerCredentials::CreateFake) { - NanScope(); - NanReturnValue( - WrapStruct(grpc_fake_transport_security_server_credentials_create())); -} - } // namespace node } // namespace grpc diff --git a/src/node/ext/server_credentials.h b/src/node/ext/server_credentials.h index aaa7ef297a..80747504a1 100644 --- a/src/node/ext/server_credentials.h +++ b/src/node/ext/server_credentials.h @@ -63,7 +63,6 @@ class ServerCredentials : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(CreateSsl); - static NAN_METHOD(CreateFake); static NanCallback *constructor; // Used for typechecking instances of this javascript class static v8::Persistent fun_tpl; diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js index 7cb34fa0cb..9c7bb465aa 100644 --- a/src/node/test/server_test.js +++ b/src/node/test/server_test.js @@ -34,6 +34,8 @@ 'use strict'; var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); var grpc = require('bindings')('grpc.node'); describe('server', function() { @@ -67,9 +69,13 @@ describe('server', function() { before(function() { server = new grpc.Server(); }); - it('should bind to an unused port with fake credentials', function() { + it('should bind to an unused port with ssl credentials', function() { var port; - var creds = grpc.ServerCredentials.createFake(); + var key_path = path.join(__dirname, '../test/data/server1.key'); + var pem_path = path.join(__dirname, '../test/data/server1.pem'); + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + var creds = grpc.ServerCredentials.createSsl(null, key_data, pem_data); assert.doesNotThrow(function() { port = server.addSecureHttp2Port('0.0.0.0:0', creds); }); diff --git a/src/php/ext/grpc/credentials.c b/src/php/ext/grpc/credentials.c index 3e781d4747..01cb94e3aa 100644 --- a/src/php/ext/grpc/credentials.c +++ b/src/php/ext/grpc/credentials.c @@ -175,7 +175,6 @@ PHP_METHOD(Credentials, createGce) { RETURN_DESTROY_ZVAL(creds_object); } - static zend_function_entry credentials_methods[] = { PHP_ME(Credentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(Credentials, createSsl, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index c4c1fabb1a..4c4a598cb0 100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -121,21 +121,8 @@ PHP_METHOD(ServerCredentials, createSsl) { RETURN_DESTROY_ZVAL(creds_object); } -/** - * Create fake credentials. Only to be used for testing. - * @return ServerCredentials The new fake credentials object - */ -PHP_METHOD(ServerCredentials, createFake) { - grpc_server_credentials *creds = - grpc_fake_transport_security_server_credentials_create(); - zval *creds_object = grpc_php_wrap_server_credentials(creds); - RETURN_DESTROY_ZVAL(creds_object); -} - static zend_function_entry server_credentials_methods[] = { PHP_ME(ServerCredentials, createSsl, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(ServerCredentials, createFake, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_server_credentials(TSRMLS_D) { diff --git a/src/python/src/grpc/_adapter/_c/types.h b/src/python/src/grpc/_adapter/_c/types.h index 3449f0643f..4e0da4a28a 100644 --- a/src/python/src/grpc/_adapter/_c/types.h +++ b/src/python/src/grpc/_adapter/_c/types.h @@ -63,8 +63,6 @@ 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; @@ -81,8 +79,6 @@ typedef struct 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; diff --git a/src/python/src/grpc/_adapter/_c/types/server_credentials.c b/src/python/src/grpc/_adapter/_c/types/server_credentials.c index 2e02c8fe81..f22edbf187 100644 --- a/src/python/src/grpc/_adapter/_c/types/server_credentials.c +++ b/src/python/src/grpc/_adapter/_c/types/server_credentials.c @@ -43,9 +43,6 @@ 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[] = ""; @@ -137,10 +134,3 @@ ServerCredentials *pygrpc_ServerCredentials_ssl( 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; -} - diff --git a/src/python/src/grpc/_adapter/_c_test.py b/src/python/src/grpc/_adapter/_c_test.py index 133b124072..fe020e2a9c 100644 --- a/src/python/src/grpc/_adapter/_c_test.py +++ b/src/python/src/grpc/_adapter/_c_test.py @@ -36,14 +36,6 @@ from grpc._adapter import _types class CTypeSmokeTest(unittest.TestCase): - def testClientCredentialsUpDown(self): - credentials = _c.ClientCredentials.fake_transport_security() - del credentials - - def testServerCredentialsUpDown(self): - credentials = _c.ServerCredentials.fake_transport_security() - del credentials - def testCompletionQueueUpDown(self): completion_queue = _c.CompletionQueue() del completion_queue @@ -58,10 +50,6 @@ class CTypeSmokeTest(unittest.TestCase): channel = _c.Channel('[::]:0', []) del channel - def testSecureChannelUpDown(self): - channel = _c.Channel('[::]:0', [], _c.ClientCredentials.fake_transport_security()) - del channel - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_cython/adapter_low.py b/src/python/src/grpc/_cython/adapter_low.py index 7546dd1599..2bb468eece 100644 --- a/src/python/src/grpc/_cython/adapter_low.py +++ b/src/python/src/grpc/_cython/adapter_low.py @@ -71,10 +71,6 @@ class ClientCredentials(object): def refresh_token(): raise NotImplementedError() - @staticmethod - def fake_transport_security(): - raise NotImplementedError() - @staticmethod def iam(): raise NotImplementedError() @@ -88,10 +84,6 @@ class ServerCredentials(object): def ssl(): raise NotImplementedError() - @staticmethod - def fake_transport_security(): - raise NotImplementedError() - class CompletionQueue(type_interfaces.CompletionQueue): def __init__(self): diff --git a/src/python/src/grpc/_cython/cygrpc.pyx b/src/python/src/grpc/_cython/cygrpc.pyx index dcb06f345c..f4d9661580 100644 --- a/src/python/src/grpc/_cython/cygrpc.pyx +++ b/src/python/src/grpc/_cython/cygrpc.pyx @@ -82,12 +82,8 @@ client_credentials_compute_engine = ( credentials.client_credentials_compute_engine) client_credentials_jwt = credentials.client_credentials_jwt client_credentials_refresh_token = credentials.client_credentials_refresh_token -client_credentials_fake_transport_security = ( - credentials.client_credentials_fake_transport_security) client_credentials_iam = credentials.client_credentials_iam server_credentials_ssl = credentials.server_credentials_ssl -server_credentials_fake_transport_security = ( - credentials.server_credentials_fake_transport_security) CompletionQueue = completion_queue.CompletionQueue Channel = channel.Channel diff --git a/src/python/src/grpc/_cython/cygrpc_test.py b/src/python/src/grpc/_cython/cygrpc_test.py index 838e1e2254..22d210b16b 100644 --- a/src/python/src/grpc/_cython/cygrpc_test.py +++ b/src/python/src/grpc/_cython/cygrpc_test.py @@ -76,14 +76,6 @@ class TypeSmokeTest(unittest.TestCase): timespec = cygrpc.Timespec(now) self.assertAlmostEqual(now, float(timespec), places=8) - def testClientCredentialsUpDown(self): - credentials = cygrpc.client_credentials_fake_transport_security() - del credentials - - def testServerCredentialsUpDown(self): - credentials = cygrpc.server_credentials_fake_transport_security() - del credentials - def testCompletionQueueUpDown(self): completion_queue = cygrpc.CompletionQueue() del completion_queue @@ -96,12 +88,6 @@ class TypeSmokeTest(unittest.TestCase): channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) del channel - def testSecureChannelUpDown(self): - channel = cygrpc.Channel( - '[::]:0', cygrpc.ChannelArgs([]), - cygrpc.client_credentials_fake_transport_security()) - del channel - @unittest.skip('TODO(atash): undo skip after #2229 is merged') def testServerStartNoExplicitShutdown(self): server = cygrpc.Server() -- cgit v1.2.3 From b6d613730f2b0d8f47973f7be578c3665ec1365c Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Wed, 22 Jul 2015 14:04:45 -0700 Subject: Fix Python C89 pedantry --- src/python/src/grpc/_adapter/_c/utility.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/src/grpc/_adapter/_c/utility.c b/src/python/src/grpc/_adapter/_c/utility.c index 000c8d0c38..d9f911a41a 100644 --- a/src/python/src/grpc/_adapter/_c/utility.c +++ b/src/python/src/grpc/_adapter/_c/utility.c @@ -489,10 +489,10 @@ PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { 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_init(&reader, buffer); gpr_slice slice; char *read_result = NULL; size_t size = 0; + grpc_byte_buffer_reader_init(&reader, buffer); 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), -- cgit v1.2.3 From fe4c3f4f1436e2fc2297d5544859ce14e03659bf Mon Sep 17 00:00:00 2001 From: Julien Boeuf Date: Wed, 22 Jul 2015 16:20:13 -0700 Subject: Renaming jwt_credentials to service_account_jwt_access_credentials. --- include/grpc++/credentials.h | 6 +++--- include/grpc/grpc_security.h | 4 ++-- src/core/security/credentials.c | 23 ++++++++++++---------- src/core/security/credentials.h | 5 +++-- src/core/security/google_default_credentials.c | 5 +++-- src/cpp/client/secure_credentials.cc | 8 ++++---- .../grpc/_adapter/_c/types/client_credentials.c | 3 ++- .../src/grpc/_cython/_cygrpc/credentials.pyx | 3 ++- src/python/src/grpc/_cython/_cygrpc/grpc.pxd | 2 +- test/core/security/credentials_test.c | 14 +++++++------ test/cpp/interop/client_helper.cc | 3 ++- 11 files changed, 43 insertions(+), 33 deletions(-) (limited to 'src/python') diff --git a/include/grpc++/credentials.h b/include/grpc++/credentials.h index 0eaaefcbca..a4f1e73118 100644 --- a/include/grpc++/credentials.h +++ b/include/grpc++/credentials.h @@ -106,13 +106,13 @@ std::shared_ptr ServiceAccountCredentials( const grpc::string& json_key, const grpc::string& scope, long token_lifetime_seconds); -// Builds JWT credentials. +// Builds Service Account JWT Access credentials. // json_key is the JSON key string containing the client's private key. // token_lifetime_seconds is the lifetime in seconds of each Json Web Token // (JWT) created with this credentials. It should not exceed // grpc_max_auth_token_lifetime or will be cropped to this value. -std::shared_ptr JWTCredentials(const grpc::string& json_key, - long token_lifetime_seconds); +std::shared_ptr ServiceAccountJWTAccessCredentials( + const grpc::string& json_key, long token_lifetime_seconds); // Builds refresh token credentials. // json_refresh_token is the JSON string containing the refresh token along diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index 37d66c04ae..4dd058063d 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -119,8 +119,8 @@ grpc_credentials *grpc_service_account_credentials_create( - token_lifetime is the lifetime of each Json Web Token (JWT) created with this credentials. It should not exceed grpc_max_auth_token_lifetime or will be cropped to this value. */ -grpc_credentials *grpc_jwt_credentials_create(const char *json_key, - gpr_timespec token_lifetime); +grpc_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, gpr_timespec token_lifetime); /* Creates an Oauth2 Refresh Token credentials object. May return NULL if the input is invalid. diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index fb59fa4b0e..38612cf308 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -315,7 +315,7 @@ grpc_server_credentials *grpc_ssl_server_credentials_create( /* -- Jwt credentials -- */ -static void jwt_reset_cache(grpc_jwt_credentials *c) { +static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) { if (c->cached.jwt_md != NULL) { grpc_credentials_md_store_unref(c->cached.jwt_md); c->cached.jwt_md = NULL; @@ -328,7 +328,8 @@ static void jwt_reset_cache(grpc_jwt_credentials *c) { } static void jwt_destroy(grpc_credentials *creds) { - grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds; + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; grpc_auth_json_key_destruct(&c->key); jwt_reset_cache(c); gpr_mu_destroy(&c->cache_mu); @@ -346,7 +347,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds, const char *service_url, grpc_credentials_metadata_cb cb, void *user_data) { - grpc_jwt_credentials *c = (grpc_jwt_credentials *)creds; + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; gpr_timespec refresh_threshold = gpr_time_from_seconds( GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); @@ -399,15 +401,16 @@ static grpc_credentials_vtable jwt_vtable = { jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only, jwt_get_request_metadata, NULL}; -grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key( +grpc_credentials * +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key key, gpr_timespec token_lifetime) { - grpc_jwt_credentials *c; + grpc_service_account_jwt_access_credentials *c; if (!grpc_auth_json_key_is_valid(&key)) { gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); return NULL; } - c = gpr_malloc(sizeof(grpc_jwt_credentials)); - memset(c, 0, sizeof(grpc_jwt_credentials)); + c = gpr_malloc(sizeof(grpc_service_account_jwt_access_credentials)); + memset(c, 0, sizeof(grpc_service_account_jwt_access_credentials)); c->base.type = GRPC_CREDENTIALS_TYPE_JWT; gpr_ref_init(&c->base.refcount, 1); c->base.vtable = &jwt_vtable; @@ -418,9 +421,9 @@ grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key( return &c->base; } -grpc_credentials *grpc_jwt_credentials_create(const char *json_key, - gpr_timespec token_lifetime) { - return grpc_jwt_credentials_create_from_auth_json_key( +grpc_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, gpr_timespec token_lifetime) { + return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key_create_from_string(json_key), token_lifetime); } diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h index d988901cf7..7f4141967d 100644 --- a/src/core/security/credentials.h +++ b/src/core/security/credentials.h @@ -188,7 +188,8 @@ grpc_credentials *grpc_fake_oauth2_credentials_create( /* Private constructor for jwt credentials from an already parsed json key. Takes ownership of the key. */ -grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key( +grpc_credentials * +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key key, gpr_timespec token_lifetime); /* Private constructor for refresh token credentials from an already parsed @@ -240,7 +241,7 @@ typedef struct { grpc_auth_json_key key; gpr_timespec jwt_lifetime; -} grpc_jwt_credentials; +} grpc_service_account_jwt_access_credentials; /* -- Oauth2TokenFetcher credentials -- diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c index 833484310f..de1929fe76 100644 --- a/src/core/security/google_default_credentials.c +++ b/src/core/security/google_default_credentials.c @@ -140,8 +140,9 @@ static grpc_credentials *create_default_creds_from_path(char *creds_path) { /* First, try an auth json key. */ key = grpc_auth_json_key_create_from_json(json); if (grpc_auth_json_key_is_valid(&key)) { - result = grpc_jwt_credentials_create_from_auth_json_key( - key, grpc_max_auth_token_lifetime); + result = + grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + key, grpc_max_auth_token_lifetime); goto end; } diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 01c7f14f1a..abf0cb387e 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -99,8 +99,8 @@ std::shared_ptr ServiceAccountCredentials( } // Builds JWT credentials. -std::shared_ptr JWTCredentials(const grpc::string& json_key, - long token_lifetime_seconds) { +std::shared_ptr ServiceAccountJWTAccessCredentials( + const grpc::string& json_key, long token_lifetime_seconds) { if (token_lifetime_seconds <= 0) { gpr_log(GPR_ERROR, "Trying to create JWTCredentials with non-positive lifetime"); @@ -108,8 +108,8 @@ std::shared_ptr JWTCredentials(const grpc::string& json_key, } gpr_timespec lifetime = gpr_time_from_seconds(token_lifetime_seconds, GPR_TIMESPAN); - return WrapCredentials( - grpc_jwt_credentials_create(json_key.c_str(), lifetime)); + return WrapCredentials(grpc_service_account_jwt_access_credentials_create( + json_key.c_str(), lifetime)); } // Builds refresh token credentials. diff --git a/src/python/src/grpc/_adapter/_c/types/client_credentials.c b/src/python/src/grpc/_adapter/_c/types/client_credentials.c index 6a4561c060..9ea2b39cad 100644 --- a/src/python/src/grpc/_adapter/_c/types/client_credentials.c +++ b/src/python/src/grpc/_adapter/_c/types/client_credentials.c @@ -208,6 +208,7 @@ ClientCredentials *pygrpc_ClientCredentials_service_account( return self; } +/* TODO: Rename this credentials to something like service_account_jwt_access */ ClientCredentials *pygrpc_ClientCredentials_jwt( PyTypeObject *type, PyObject *args, PyObject *kwargs) { ClientCredentials *self; @@ -219,7 +220,7 @@ ClientCredentials *pygrpc_ClientCredentials_jwt( return NULL; } self = (ClientCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_jwt_credentials_create( + self->c_creds = grpc_service_account_jwt_access_credentials_create( json_key, pygrpc_cast_double_to_gpr_timespec(lifetime)); if (!self->c_creds) { Py_DECREF(self); diff --git a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx b/src/python/src/grpc/_cython/_cygrpc/credentials.pyx index c14d8844dd..7bb3f798b2 100644 --- a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx +++ b/src/python/src/grpc/_cython/_cygrpc/credentials.pyx @@ -126,6 +126,7 @@ def client_credentials_service_account( credentials.references.extend([json_key, scope]) return credentials +#TODO rename to something like client_credentials_service_account_jwt_access. def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): if isinstance(json_key, bytes): pass @@ -134,7 +135,7 @@ def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): else: raise TypeError("expected json_key to be str or bytes") cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_jwt_credentials_create( + credentials.c_credentials = grpc.grpc_service_account_jwt_access_credentials_create( json_key, token_lifetime.c_time) credentials.references.append(json_key) return credentials diff --git a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd b/src/python/src/grpc/_cython/_cygrpc/grpc.pxd index 7db8fbe31c..a76ddfc9e1 100644 --- a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd +++ b/src/python/src/grpc/_cython/_cygrpc/grpc.pxd @@ -313,7 +313,7 @@ cdef extern from "grpc/grpc_security.h": grpc_credentials *grpc_compute_engine_credentials_create() grpc_credentials *grpc_service_account_credentials_create( const char *json_key, const char *scope, gpr_timespec token_lifetime) - grpc_credentials *grpc_jwt_credentials_create(const char *json_key, + grpc_credentials *grpc_service_account_jwt_access_credentials_create(const char *json_key, gpr_timespec token_lifetime) grpc_credentials *grpc_refresh_token_credentials_create( const char *json_refresh_token) diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c index d3fea9680a..dd6e0d7bb3 100644 --- a/test/core/security/credentials_test.c +++ b/test/core/security/credentials_test.c @@ -826,8 +826,9 @@ static void on_jwt_creds_get_metadata_failure(void *user_data, static void test_jwt_creds_success(void) { char *json_key_string = test_json_key_str(); - grpc_credentials *jwt_creds = grpc_jwt_credentials_create( - json_key_string, grpc_max_auth_token_lifetime); + grpc_credentials *jwt_creds = + grpc_service_account_jwt_access_credentials_create( + json_key_string, grpc_max_auth_token_lifetime); GPR_ASSERT(grpc_credentials_has_request_metadata(jwt_creds)); GPR_ASSERT(grpc_credentials_has_request_metadata_only(jwt_creds)); @@ -858,8 +859,9 @@ static void test_jwt_creds_success(void) { static void test_jwt_creds_signing_failure(void) { char *json_key_string = test_json_key_str(); - grpc_credentials *jwt_creds = grpc_jwt_credentials_create( - json_key_string, grpc_max_auth_token_lifetime); + grpc_credentials *jwt_creds = + grpc_service_account_jwt_access_credentials_create( + json_key_string, grpc_max_auth_token_lifetime); GPR_ASSERT(grpc_credentials_has_request_metadata(jwt_creds)); GPR_ASSERT(grpc_credentials_has_request_metadata_only(jwt_creds)); @@ -900,7 +902,7 @@ static grpc_credentials *composite_inner_creds(grpc_credentials *creds, } static void test_google_default_creds_auth_key(void) { - grpc_jwt_credentials *jwt; + grpc_service_account_jwt_access_credentials *jwt; grpc_credentials *creds; char *json_key = test_json_key_str(); grpc_flush_cached_google_default_credentials(); @@ -909,7 +911,7 @@ static void test_google_default_creds_auth_key(void) { gpr_free(json_key); creds = grpc_google_default_credentials_create(); GPR_ASSERT(creds != NULL); - jwt = (grpc_jwt_credentials *)composite_inner_creds( + jwt = (grpc_service_account_jwt_access_credentials *)composite_inner_creds( creds, GRPC_CREDENTIALS_TYPE_JWT); GPR_ASSERT( strcmp(jwt->key.client_id, diff --git a/test/cpp/interop/client_helper.cc b/test/cpp/interop/client_helper.cc index 48b1b2e864..73d82f7b88 100644 --- a/test/cpp/interop/client_helper.cc +++ b/test/cpp/interop/client_helper.cc @@ -123,7 +123,8 @@ std::shared_ptr CreateChannelForTestCase( GPR_ASSERT(FLAGS_enable_ssl); grpc::string json_key = GetServiceAccountJsonKey(); std::chrono::seconds token_lifetime = std::chrono::hours(1); - creds = JWTCredentials(json_key, token_lifetime.count()); + creds = + ServiceAccountJWTAccessCredentials(json_key, token_lifetime.count()); return CreateTestChannel(host_port, FLAGS_server_host_override, FLAGS_enable_ssl, FLAGS_use_prod_roots, creds); } else if (test_case == "oauth2_auth_token") { -- cgit v1.2.3 From 94329d09656f3eeb8eee40b72b96ec9cd3578559 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 23 Jul 2015 09:52:11 -0700 Subject: Make the server report monotonic times for deadlines For very high performance systems, we're going to want to be able to simply push the value reported from the server down onto clients. If we report realtime now, then all wrapped languages are going to assume it, meaning that such a change will be impossible later. --- src/core/surface/call.c | 3 ++- src/core/surface/server.c | 6 ++++-- src/core/transport/chttp2/parsing.c | 2 +- src/cpp/util/time.cc | 3 ++- src/node/ext/timeval.cc | 1 + src/python/src/grpc/_adapter/_c/utility.c | 1 + src/ruby/ext/grpc/rb_grpc.c | 6 ++++-- src/ruby/ext/grpc/rb_server.c | 13 ++++++------- 8 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src/python') diff --git a/src/core/surface/call.c b/src/core/surface/call.c index e08273e451..1f73f9cf71 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -1368,7 +1368,8 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) { l->md = 0; } } - if (gpr_time_cmp(md->deadline, gpr_inf_future(GPR_CLOCK_REALTIME)) != 0) { + if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != + 0) { set_deadline_alarm(call, md->deadline); } if (!is_trailing) { diff --git a/src/core/surface/server.c b/src/core/surface/server.c index 439452aea2..a0d4ab3b0a 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -530,6 +530,7 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { static void server_on_recv(void *ptr, int success) { grpc_call_element *elem = ptr; call_data *calld = elem->call_data; + gpr_timespec op_deadline; if (success && !calld->got_initial_metadata) { size_t i; @@ -539,8 +540,9 @@ static void server_on_recv(void *ptr, int success) { grpc_stream_op *op = &ops[i]; if (op->type != GRPC_OP_METADATA) continue; grpc_metadata_batch_filter(&op->data.metadata, server_filter, elem); - if (0 != gpr_time_cmp(op->data.metadata.deadline, - gpr_inf_future(GPR_CLOCK_REALTIME))) { + op_deadline = op->data.metadata.deadline; + if (0 != + gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) { calld->deadline = op->data.metadata.deadline; } calld->got_initial_metadata = 1; diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c index 904b9afce7..50a2f752f6 100644 --- a/src/core/transport/chttp2/parsing.c +++ b/src/core/transport/chttp2/parsing.c @@ -607,7 +607,7 @@ static void on_header(void *tp, grpc_mdelem *md) { } grpc_chttp2_incoming_metadata_buffer_set_deadline( &stream_parsing->incoming_metadata, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), *cached_timeout)); + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), *cached_timeout)); GRPC_MDELEM_UNREF(md); } else { grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->incoming_metadata, diff --git a/src/cpp/util/time.cc b/src/cpp/util/time.cc index a814cad452..799c597e0b 100644 --- a/src/cpp/util/time.cc +++ b/src/cpp/util/time.cc @@ -79,9 +79,10 @@ void TimepointHR2Timespec(const high_resolution_clock::time_point& from, } system_clock::time_point Timespec2Timepoint(gpr_timespec t) { - if (gpr_time_cmp(t, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { + if (gpr_time_cmp(t, gpr_inf_future(t.clock_type)) == 0) { return system_clock::time_point::max(); } + t = gpr_convert_clock_type(t, GPR_CLOCK_REALTIME); system_clock::time_point tp; tp += duration_cast(seconds(t.tv_sec)); tp += diff --git a/src/node/ext/timeval.cc b/src/node/ext/timeval.cc index 60de4d816d..bf68513c48 100644 --- a/src/node/ext/timeval.cc +++ b/src/node/ext/timeval.cc @@ -52,6 +52,7 @@ gpr_timespec MillisecondsToTimespec(double millis) { } double TimespecToMilliseconds(gpr_timespec timespec) { + timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); if (gpr_time_cmp(timespec, gpr_inf_future(GPR_CLOCK_REALTIME)) == 0) { return std::numeric_limits::infinity(); } else if (gpr_time_cmp(timespec, gpr_inf_past(GPR_CLOCK_REALTIME)) == 0) { diff --git a/src/python/src/grpc/_adapter/_c/utility.c b/src/python/src/grpc/_adapter/_c/utility.c index d9f911a41a..51f3c9be01 100644 --- a/src/python/src/grpc/_adapter/_c/utility.c +++ b/src/python/src/grpc/_adapter/_c/utility.c @@ -374,6 +374,7 @@ PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) { } double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) { + timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); return timespec.tv_sec + 1e-9*timespec.tv_nsec; } diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 829f825597..65d9c9a237 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -209,10 +209,12 @@ static ID id_to_s; /* Converts a wrapped time constant to a standard time. */ static VALUE grpc_rb_time_val_to_time(VALUE self) { gpr_timespec *time_const = NULL; + gpr_timespec real_time; TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type, time_const); - return rb_funcall(rb_cTime, id_at, 2, INT2NUM(time_const->tv_sec), - INT2NUM(time_const->tv_nsec)); + real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME); + return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec), + INT2NUM(real_time.tv_nsec)); } /* Invokes inspect on the ctime version of the time val. */ diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index e3a0a5ad80..375a651d24 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -213,6 +213,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue, grpc_call_error err; request_call_stack st; VALUE result; + gpr_timespec deadline; TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s); if (s->wrapped == NULL) { rb_raise(rb_eRuntimeError, "destroyed!"); @@ -245,15 +246,13 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue, } /* build the NewServerRpc struct result */ + deadline = gpr_convert_clock_type(st.details.deadline, GPR_CLOCK_REALTIME); result = rb_struct_new( - grpc_rb_sNewServerRpc, - rb_str_new2(st.details.method), + grpc_rb_sNewServerRpc, rb_str_new2(st.details.method), rb_str_new2(st.details.host), - rb_funcall(rb_cTime, id_at, 2, INT2NUM(st.details.deadline.tv_sec), - INT2NUM(st.details.deadline.tv_nsec)), - grpc_rb_md_ary_to_h(&st.md_ary), - grpc_rb_wrap_call(call), - NULL); + rb_funcall(rb_cTime, id_at, 2, INT2NUM(deadline.tv_sec), + INT2NUM(deadline.tv_nsec)), + grpc_rb_md_ary_to_h(&st.md_ary), grpc_rb_wrap_call(call), NULL); grpc_request_call_stack_cleanup(&st); return result; } -- cgit v1.2.3 From e2f2e9a31a39caf56a216db0c53c74a0a52606de Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Thu, 23 Jul 2015 11:35:10 -0700 Subject: Integration fix --- src/python/src/grpc/_adapter/_low_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/python') diff --git a/src/python/src/grpc/_adapter/_low_test.py b/src/python/src/grpc/_adapter/_low_test.py index a49cd007bf..9a8edfad0c 100644 --- a/src/python/src/grpc/_adapter/_low_test.py +++ b/src/python/src/grpc/_adapter/_low_test.py @@ -97,7 +97,7 @@ class InsecureServerInsecureClient(unittest.TestCase): 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_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' -- cgit v1.2.3 From 7b042c5793b1477f4a0a1a1ee72796ea53bf88ae Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Mon, 27 Jul 2015 20:59:36 +0000 Subject: Add STREAM_LENGTH and POOL_SIZE to test_constants Only one of these is used, and in only one place, as of this commit, but they will be used widely after further development. --- src/python/src/grpc/_links/_proto_scenarios.py | 4 ++-- src/python/src/grpc/framework/common/test_constants.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/python') diff --git a/src/python/src/grpc/_links/_proto_scenarios.py b/src/python/src/grpc/_links/_proto_scenarios.py index ccf3c29782..320c0e0f50 100644 --- a/src/python/src/grpc/_links/_proto_scenarios.py +++ b/src/python/src/grpc/_links/_proto_scenarios.py @@ -33,6 +33,7 @@ import abc import threading from grpc._junkdrawer import math_pb2 +from grpc.framework.common import test_constants class ProtoScenario(object): @@ -219,10 +220,9 @@ class BidirectionallyUnaryScenario(ProtoScenario): class BidirectionallyStreamingScenario(ProtoScenario): """A scenario that transmits no protocol buffers in either direction.""" - _STREAM_LENGTH = 200 _REQUESTS = tuple( math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) - for index in range(_STREAM_LENGTH)) + for index in range(test_constants.STREAM_LENGTH)) def __init__(self): self._lock = threading.Lock() diff --git a/src/python/src/grpc/framework/common/test_constants.py b/src/python/src/grpc/framework/common/test_constants.py index 237b8754ed..3126d0d82c 100644 --- a/src/python/src/grpc/framework/common/test_constants.py +++ b/src/python/src/grpc/framework/common/test_constants.py @@ -35,3 +35,9 @@ SHORT_TIMEOUT = 4 # Absurdly large value for maximum duration in seconds for should-not-time-out # RPCs made during tests. LONG_TIMEOUT = 3000 + +# The number of payloads to transmit in streaming tests. +STREAM_LENGTH = 200 + +# The size of thread pools to use in tests. +POOL_SIZE = 10 -- cgit v1.2.3 From d65632ab044dce63c269e4d1eb432689374015b2 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 27 Jul 2015 14:30:09 -0700 Subject: Add Python documentation generation --- src/python/src/.gitignore | 4 ++ src/python/src/MANIFEST.in | 1 + src/python/src/commands.py | 75 ++++++++++++++++++++++++++ src/python/src/setup.cfg | 2 + src/python/src/setup.py | 28 ++++++++-- tools/distrib/python/.gitignore | 1 + tools/distrib/python/docgen.py | 113 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+), 5 deletions(-) create mode 100644 src/python/src/commands.py create mode 100644 src/python/src/setup.cfg create mode 100644 tools/distrib/python/.gitignore create mode 100755 tools/distrib/python/docgen.py (limited to 'src/python') diff --git a/src/python/src/.gitignore b/src/python/src/.gitignore index 144e501237..d89f3db999 100644 --- a/src/python/src/.gitignore +++ b/src/python/src/.gitignore @@ -2,3 +2,7 @@ MANIFEST grpcio.egg-info/ build/ dist/ +*.egg +*.egg/ +*.eggs/ +doc/ diff --git a/src/python/src/MANIFEST.in b/src/python/src/MANIFEST.in index 6f32db0548..498b55f20a 100644 --- a/src/python/src/MANIFEST.in +++ b/src/python/src/MANIFEST.in @@ -1 +1,2 @@ graft grpc +include commands.py diff --git a/src/python/src/commands.py b/src/python/src/commands.py new file mode 100644 index 0000000000..8e87855011 --- /dev/null +++ b/src/python/src/commands.py @@ -0,0 +1,75 @@ +# 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. + +"""Provides distutils command classes for the GRPC Python setup process.""" + +import os +import os.path +import sys + +import setuptools + +_CONF_PY_ADDENDUM = """ +extensions.append('sphinx.ext.napoleon') +napoleon_google_docstring = True +napoleon_numpy_docstring = True + +html_theme = 'sphinx_rtd_theme' +""" + +class SphinxDocumentation(setuptools.Command): + """Command to generate documentation via sphinx.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # We import here to ensure that setup.py has had a chance to install the + # relevant package eggs first. + import sphinx + import sphinx.apidoc + metadata = self.distribution.metadata + src_dir = os.path.join( + os.getcwd(), self.distribution.package_dir['grpc']) + sys.path.append(src_dir) + sphinx.apidoc.main([ + '', '--force', '--full', '-H', metadata.name, '-A', metadata.author, + '-V', metadata.version, '-R', metadata.version, + '-o', os.path.join('doc', 'src'), src_dir]) + conf_filepath = os.path.join('doc', 'src', 'conf.py') + with open(conf_filepath, 'a') as conf_file: + conf_file.write(_CONF_PY_ADDENDUM) + sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) + diff --git a/src/python/src/setup.cfg b/src/python/src/setup.cfg new file mode 100644 index 0000000000..8f69613632 --- /dev/null +++ b/src/python/src/setup.cfg @@ -0,0 +1,2 @@ +[build_ext] +inplace=1 diff --git a/src/python/src/setup.py b/src/python/src/setup.py index a857ae98cc..0310a83a7b 100644 --- a/src/python/src/setup.py +++ b/src/python/src/setup.py @@ -30,11 +30,17 @@ """A setup module for the GRPC Python package.""" import os +import os.path import sys from distutils import core as _core import setuptools +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands # Use environment variables to determine whether or not the Cython extension # should *use* Cython or use the generated C files. Note that this requires the @@ -98,15 +104,27 @@ _PACKAGE_DIRECTORIES = { 'grpc.framework': 'grpc/framework', } +_INSTALL_REQUIRES = ( + 'enum34==1.0.4', + 'futures==2.2.0', + 'protobuf==3.0.0a3' +) + +_SETUP_REQUIRES = ( + 'sphinx>=1.3', +) + _INSTALL_REQUIRES + +_COMMAND_CLASS = { + 'doc': commands.SphinxDocumentation +} + setuptools.setup( name='grpcio', version='0.10.0a0', ext_modules=_EXTENSION_MODULES, packages=list(_PACKAGES), package_dir=_PACKAGE_DIRECTORIES, - install_requires=[ - 'enum34==1.0.4', - 'futures==2.2.0', - 'protobuf==3.0.0a3' - ] + install_requires=_INSTALL_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS ) diff --git a/tools/distrib/python/.gitignore b/tools/distrib/python/.gitignore new file mode 100644 index 0000000000..8ff3b086e3 --- /dev/null +++ b/tools/distrib/python/.gitignore @@ -0,0 +1 @@ +distrib_virtualenv/ diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py new file mode 100755 index 0000000000..3ab84a6ba1 --- /dev/null +++ b/tools/distrib/python/docgen.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# 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 argparse +import os +import os.path +import shutil +import subprocess +import tempfile + +parser = argparse.ArgumentParser() +parser.add_argument('--config', metavar='c', type=str, nargs=1, + help='GRPC/GPR libraries build configuration', + default='opt') +parser.add_argument('--submit', action='store_true') +parser.add_argument('--gh-user', type=str, help='GitHub user to push as.') +parser.add_argument('--gh-repo-owner', type=str, + help=('Owner of the GitHub repository to be pushed; ' + 'defaults to --gh-user.')) +parser.add_argument('--doc-branch', type=str) +args = parser.parse_args() + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..')) + +CONFIG = args.config +SETUP_PATH = os.path.join(PROJECT_ROOT, 'src/python/src/setup.py') +DOC_PATH = os.path.join(PROJECT_ROOT, 'src/python/src/doc/build') +INCLUDE_PATH = os.path.join(PROJECT_ROOT, 'include') +LIBRARY_PATH = os.path.join(PROJECT_ROOT, 'libs/{}'.format(CONFIG)) +VIRTUALENV_DIR = os.path.join(SCRIPT_DIR, 'distrib_virtualenv') +VIRTUALENV_PYTHON_PATH = os.path.join(VIRTUALENV_DIR, 'bin', 'python') + +environment = os.environ.copy() +environment.update({ + 'CONFIG': CONFIG, + 'CFLAGS': '-I{}'.format(INCLUDE_PATH), + 'LDFLAGS': '-L{}'.format(LIBRARY_PATH), + 'LD_LIBRARY_PATH': LIBRARY_PATH +}) + +subprocess_arguments_list = [ + {'args': ['make'], 'cwd': PROJECT_ROOT}, + {'args': ['virtualenv', VIRTUALENV_DIR], 'env': environment}, + {'args': [VIRTUALENV_PYTHON_PATH, SETUP_PATH, 'build'], 'env': environment}, + {'args': [VIRTUALENV_PYTHON_PATH, SETUP_PATH, 'doc'], 'env': environment}, +] + +for subprocess_arguments in subprocess_arguments_list: + subprocess.check_call(**subprocess_arguments) + +if args.submit: + assert args.gh_user + assert args.doc_branch + github_user = args.gh_user + github_repository_owner = ( + args.gh_repo_owner if args.gh_repo_owner else gh_user) + # Create a temporary directory out of tree, checkout gh-pages from the + # specified repository, edit it, and push it. It's up to the user to then go + # onto GitHub and make a PR against grpc/grpc:gh-pages. + repo_parent_dir = tempfile.mkdtemp() + repo_dir = os.path.join(repo_parent_dir, 'grpc') + python_doc_dir = os.path.join(repo_dir, 'python') + doc_branch = args.doc_branch + + subprocess.check_call([ + 'git', 'clone', 'https://{}@github.com/{}/grpc'.format( + github_user, github_repository_owner) + ], cwd=repo_parent_dir) + subprocess.check_call([ + 'git', 'remote', 'add', 'upstream', 'https://github.com/grpc/grpc' + ], cwd=repo_dir) + subprocess.check_call(['git', 'fetch', 'upstream'], cwd=repo_dir) + subprocess.check_call([ + 'git', 'checkout', 'upstream/gh-pages', '-b', doc_branch + ], cwd=repo_dir) + shutil.rmtree(python_doc_dir, ignore_errors=True) + shutil.copytree(DOC_PATH, python_doc_dir) + subprocess.check_call(['git', 'add', '--all'], cwd=repo_dir) + subprocess.check_call([ + 'git', 'commit', '-m', 'Auto-update Python documentation' + ], cwd=repo_dir) + subprocess.check_call([ + 'git', 'push', '--set-upstream', 'origin', doc_branch + ], cwd=repo_dir) + shutil.rmtree(repo_parent_dir) -- cgit v1.2.3 From fe8dc883d0733a0091c6cc429e74df680749aa2d Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 27 Jul 2015 15:30:33 -0700 Subject: Reorganize Python packages This is in preparation of moving all tests into a separate package to enable ease of coverage checking and testing. --- src/python/grpcio/.gitignore | 8 + src/python/grpcio/MANIFEST.in | 2 + src/python/grpcio/README.rst | 23 + src/python/grpcio/commands.py | 76 +++ src/python/grpcio/grpc/__init__.py | 30 + src/python/grpcio/grpc/_adapter/.gitignore | 5 + src/python/grpcio/grpc/_adapter/__init__.py | 30 + .../_blocking_invocation_inline_service_test.py | 46 ++ src/python/grpcio/grpc/_adapter/_c/module.c | 61 ++ src/python/grpcio/grpc/_adapter/_c/types.c | 60 ++ src/python/grpcio/grpc/_adapter/_c/types.h | 267 +++++++++ src/python/grpcio/grpc/_adapter/_c/types/call.c | 163 ++++++ src/python/grpcio/grpc/_adapter/_c/types/channel.c | 134 +++++ .../grpc/_adapter/_c/types/client_credentials.c | 270 +++++++++ .../grpc/_adapter/_c/types/completion_queue.c | 124 ++++ src/python/grpcio/grpc/_adapter/_c/types/server.c | 180 ++++++ .../grpc/_adapter/_c/types/server_credentials.c | 138 +++++ src/python/grpcio/grpc/_adapter/_c/utility.c | 506 ++++++++++++++++ src/python/grpcio/grpc/_adapter/_c_test.py | 55 ++ src/python/grpcio/grpc/_adapter/_common.py | 76 +++ ...nt_invocation_synchronous_event_service_test.py | 46 ++ src/python/grpcio/grpc/_adapter/_face_test_case.py | 106 ++++ ...e_invocation_asynchronous_event_service_test.py | 46 ++ .../grpcio/grpc/_adapter/_intermediary_low.py | 259 +++++++++ .../grpcio/grpc/_adapter/_intermediary_low_test.py | 434 ++++++++++++++ src/python/grpcio/grpc/_adapter/_links_test.py | 277 +++++++++ .../grpcio/grpc/_adapter/_lonely_rear_link_test.py | 100 ++++ src/python/grpcio/grpc/_adapter/_low.py | 108 ++++ src/python/grpcio/grpc/_adapter/_low_test.py | 199 +++++++ .../grpcio/grpc/_adapter/_proto_scenarios.py | 261 +++++++++ src/python/grpcio/grpc/_adapter/_test_links.py | 80 +++ src/python/grpcio/grpc/_adapter/_types.py | 368 ++++++++++++ src/python/grpcio/grpc/_adapter/fore.py | 363 ++++++++++++ src/python/grpcio/grpc/_adapter/rear.py | 395 +++++++++++++ src/python/grpcio/grpc/_cython/.gitignore | 7 + src/python/grpcio/grpc/_cython/README.rst | 52 ++ src/python/grpcio/grpc/_cython/__init__.py | 28 + src/python/grpcio/grpc/_cython/_cygrpc/__init__.py | 28 + src/python/grpcio/grpc/_cython/_cygrpc/call.pxd | 37 ++ src/python/grpcio/grpc/_cython/_cygrpc/call.pyx | 82 +++ src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd | 36 ++ src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx | 84 +++ .../grpc/_cython/_cygrpc/completion_queue.pxd | 39 ++ .../grpc/_cython/_cygrpc/completion_queue.pyx | 117 ++++ .../grpcio/grpc/_cython/_cygrpc/credentials.pxd | 45 ++ .../grpcio/grpc/_cython/_cygrpc/credentials.pyx | 207 +++++++ src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd | 342 +++++++++++ src/python/grpcio/grpc/_cython/_cygrpc/records.pxd | 129 +++++ src/python/grpcio/grpc/_cython/_cygrpc/records.pyx | 575 ++++++++++++++++++ src/python/grpcio/grpc/_cython/_cygrpc/server.pxd | 45 ++ src/python/grpcio/grpc/_cython/_cygrpc/server.pyx | 158 +++++ src/python/grpcio/grpc/_cython/adapter_low.py | 106 ++++ src/python/grpcio/grpc/_cython/adapter_low_test.py | 187 ++++++ src/python/grpcio/grpc/_cython/cygrpc.pyx | 107 ++++ src/python/grpcio/grpc/_cython/cygrpc_test.py | 262 +++++++++ src/python/grpcio/grpc/_cython/test_utilities.py | 46 ++ src/python/grpcio/grpc/_junkdrawer/__init__.py | 30 + src/python/grpcio/grpc/_junkdrawer/math_pb2.py | 266 +++++++++ src/python/grpcio/grpc/_junkdrawer/stock_pb2.py | 152 +++++ src/python/grpcio/grpc/_links/__init__.py | 30 + .../grpc/_links/_lonely_invocation_link_test.py | 88 +++ src/python/grpcio/grpc/_links/_proto_scenarios.py | 261 +++++++++ .../grpcio/grpc/_links/_transmission_test.py | 231 ++++++++ src/python/grpcio/grpc/_links/invocation.py | 363 ++++++++++++ src/python/grpcio/grpc/_links/service.py | 402 +++++++++++++ src/python/grpcio/grpc/early_adopter/__init__.py | 30 + .../grpcio/grpc/early_adopter/implementations.py | 250 ++++++++ .../grpc/early_adopter/implementations_test.py | 180 ++++++ src/python/grpcio/grpc/framework/__init__.py | 30 + src/python/grpcio/grpc/framework/alpha/__init__.py | 28 + .../grpcio/grpc/framework/alpha/_face_utilities.py | 183 ++++++ .../grpcio/grpc/framework/alpha/_reexport.py | 203 +++++++ .../grpcio/grpc/framework/alpha/exceptions.py | 48 ++ .../grpcio/grpc/framework/alpha/interfaces.py | 388 +++++++++++++ .../grpcio/grpc/framework/alpha/utilities.py | 269 +++++++++ src/python/grpcio/grpc/framework/base/__init__.py | 30 + .../grpcio/grpc/framework/base/_cancellation.py | 64 +++ .../grpcio/grpc/framework/base/_constants.py | 32 ++ src/python/grpcio/grpc/framework/base/_context.py | 99 ++++ src/python/grpcio/grpc/framework/base/_emission.py | 125 ++++ src/python/grpcio/grpc/framework/base/_ends.py | 399 +++++++++++++ .../grpcio/grpc/framework/base/_expiration.py | 158 +++++ .../grpcio/grpc/framework/base/_ingestion.py | 442 ++++++++++++++ .../grpcio/grpc/framework/base/_interfaces.py | 271 +++++++++ .../grpcio/grpc/framework/base/_reception.py | 399 +++++++++++++ .../grpcio/grpc/framework/base/_termination.py | 204 +++++++ .../grpcio/grpc/framework/base/_transmission.py | 429 ++++++++++++++ .../grpcio/grpc/framework/base/exceptions.py | 34 ++ .../grpcio/grpc/framework/base/implementations.py | 77 +++ .../grpc/framework/base/implementations_test.py | 80 +++ src/python/grpcio/grpc/framework/base/in_memory.py | 108 ++++ .../grpcio/grpc/framework/base/interfaces.py | 363 ++++++++++++ .../grpc/framework/base/interfaces_test_case.py | 307 ++++++++++ src/python/grpcio/grpc/framework/base/null.py | 56 ++ src/python/grpcio/grpc/framework/base/util.py | 94 +++ .../grpcio/grpc/framework/common/__init__.py | 30 + .../grpcio/grpc/framework/common/cardinality.py | 42 ++ src/python/grpcio/grpc/framework/common/style.py | 40 ++ .../grpcio/grpc/framework/common/test_constants.py | 43 ++ .../grpcio/grpc/framework/common/test_control.py | 87 +++ .../grpcio/grpc/framework/common/test_coverage.py | 116 ++++ src/python/grpcio/grpc/framework/face/__init__.py | 30 + src/python/grpcio/grpc/framework/face/_calls.py | 422 ++++++++++++++ src/python/grpcio/grpc/framework/face/_control.py | 198 +++++++ src/python/grpcio/grpc/framework/face/_service.py | 187 ++++++ .../grpcio/grpc/framework/face/_test_case.py | 61 ++ .../blocking_invocation_inline_service_test.py | 46 ++ .../grpcio/grpc/framework/face/demonstration.py | 118 ++++ ...nt_invocation_synchronous_event_service_test.py | 46 ++ .../grpcio/grpc/framework/face/exceptions.py | 77 +++ ...e_invocation_asynchronous_event_service_test.py | 46 ++ .../grpcio/grpc/framework/face/implementations.py | 318 ++++++++++ .../grpcio/grpc/framework/face/interfaces.py | 640 +++++++++++++++++++++ .../grpcio/grpc/framework/face/testing/__init__.py | 30 + .../grpc/framework/face/testing/base_util.py | 102 ++++ ...blocking_invocation_inline_service_test_case.py | 222 +++++++ .../grpcio/grpc/framework/face/testing/callback.py | 94 +++ .../grpcio/grpc/framework/face/testing/control.py | 87 +++ .../grpcio/grpc/framework/face/testing/coverage.py | 123 ++++ .../grpcio/grpc/framework/face/testing/digest.py | 450 +++++++++++++++ ...vocation_synchronous_event_service_test_case.py | 362 ++++++++++++ ...ocation_asynchronous_event_service_test_case.py | 376 ++++++++++++ .../grpc/framework/face/testing/interfaces.py | 117 ++++ .../grpcio/grpc/framework/face/testing/serial.py | 70 +++ .../grpcio/grpc/framework/face/testing/service.py | 337 +++++++++++ .../grpc/framework/face/testing/stock_service.py | 374 ++++++++++++ .../grpc/framework/face/testing/test_case.py | 80 +++ src/python/grpcio/grpc/framework/face/utilities.py | 177 ++++++ .../grpcio/grpc/framework/foundation/__init__.py | 30 + .../grpc/framework/foundation/_later_test.py | 151 +++++ .../framework/foundation/_logging_pool_test.py | 64 +++ .../grpc/framework/foundation/_timer_future.py | 228 ++++++++ .../grpc/framework/foundation/abandonment.py | 38 ++ .../grpcio/grpc/framework/foundation/activated.py | 65 +++ .../grpc/framework/foundation/callable_util.py | 107 ++++ .../grpcio/grpc/framework/foundation/future.py | 236 ++++++++ .../grpcio/grpc/framework/foundation/later.py | 51 ++ .../grpc/framework/foundation/logging_pool.py | 83 +++ .../grpcio/grpc/framework/foundation/relay.py | 175 ++++++ .../grpcio/grpc/framework/foundation/stream.py | 60 ++ .../grpc/framework/foundation/stream_testing.py | 73 +++ .../grpc/framework/foundation/stream_util.py | 160 ++++++ .../grpcio/grpc/framework/interfaces/__init__.py | 30 + .../grpc/framework/interfaces/links/__init__.py | 30 + .../grpc/framework/interfaces/links/links.py | 124 ++++ .../grpc/framework/interfaces/links/test_cases.py | 333 +++++++++++ .../framework/interfaces/links/test_utilities.py | 66 +++ .../grpc/framework/interfaces/links/utilities.py | 44 ++ src/python/grpcio/setup.cfg | 2 + src/python/grpcio/setup.py | 112 ++++ src/python/grpcio_test/grpc_interop/__init__.py | 30 + .../grpc_interop/_insecure_interop_test.py | 57 ++ .../grpcio_test/grpc_interop/_interop_test_case.py | 61 ++ .../grpc_interop/_secure_interop_test.py | 64 +++ src/python/grpcio_test/grpc_interop/client.py | 110 ++++ .../grpcio_test/grpc_interop/credentials/README | 1 + .../grpcio_test/grpc_interop/credentials/ca.pem | 15 + .../grpc_interop/credentials/server1.key | 16 + .../grpc_interop/credentials/server1.pem | 16 + src/python/grpcio_test/grpc_interop/empty_pb2.py | 63 ++ .../grpcio_test/grpc_interop/messages_pb2.py | 447 ++++++++++++++ src/python/grpcio_test/grpc_interop/methods.py | 375 ++++++++++++ src/python/grpcio_test/grpc_interop/resources.py | 56 ++ src/python/grpcio_test/grpc_interop/server.py | 74 +++ src/python/grpcio_test/grpc_interop/test_pb2.py | 178 ++++++ src/python/grpcio_test/setup.py | 55 ++ src/python/interop/interop/__init__.py | 30 - .../interop/interop/_insecure_interop_test.py | 57 -- src/python/interop/interop/_interop_test_case.py | 61 -- src/python/interop/interop/_secure_interop_test.py | 64 --- src/python/interop/interop/client.py | 110 ---- src/python/interop/interop/credentials/README | 1 - src/python/interop/interop/credentials/ca.pem | 15 - src/python/interop/interop/credentials/server1.key | 16 - src/python/interop/interop/credentials/server1.pem | 16 - src/python/interop/interop/empty_pb2.py | 63 -- src/python/interop/interop/messages_pb2.py | 447 -------------- src/python/interop/interop/methods.py | 375 ------------ src/python/interop/interop/resources.py | 56 -- src/python/interop/interop/server.py | 74 --- src/python/interop/interop/test_pb2.py | 178 ------ src/python/interop/setup.py | 57 -- src/python/src/.gitignore | 8 - src/python/src/MANIFEST.in | 2 - src/python/src/README.rst | 23 - src/python/src/commands.py | 75 --- src/python/src/grpc/__init__.py | 30 - src/python/src/grpc/_adapter/.gitignore | 5 - src/python/src/grpc/_adapter/__init__.py | 30 - .../_blocking_invocation_inline_service_test.py | 46 -- src/python/src/grpc/_adapter/_c/module.c | 61 -- src/python/src/grpc/_adapter/_c/types.c | 60 -- src/python/src/grpc/_adapter/_c/types.h | 267 --------- src/python/src/grpc/_adapter/_c/types/call.c | 163 ------ src/python/src/grpc/_adapter/_c/types/channel.c | 134 ----- .../grpc/_adapter/_c/types/client_credentials.c | 270 --------- .../src/grpc/_adapter/_c/types/completion_queue.c | 124 ---- src/python/src/grpc/_adapter/_c/types/server.c | 180 ------ .../grpc/_adapter/_c/types/server_credentials.c | 138 ----- src/python/src/grpc/_adapter/_c/utility.c | 506 ---------------- src/python/src/grpc/_adapter/_c_test.py | 55 -- src/python/src/grpc/_adapter/_common.py | 76 --- ...nt_invocation_synchronous_event_service_test.py | 46 -- src/python/src/grpc/_adapter/_face_test_case.py | 106 ---- ...e_invocation_asynchronous_event_service_test.py | 46 -- src/python/src/grpc/_adapter/_intermediary_low.py | 259 --------- .../src/grpc/_adapter/_intermediary_low_test.py | 434 -------------- src/python/src/grpc/_adapter/_links_test.py | 277 --------- .../src/grpc/_adapter/_lonely_rear_link_test.py | 100 ---- src/python/src/grpc/_adapter/_low.py | 108 ---- src/python/src/grpc/_adapter/_low_test.py | 199 ------- src/python/src/grpc/_adapter/_proto_scenarios.py | 261 --------- src/python/src/grpc/_adapter/_test_links.py | 80 --- src/python/src/grpc/_adapter/_types.py | 368 ------------ src/python/src/grpc/_adapter/fore.py | 363 ------------ src/python/src/grpc/_adapter/rear.py | 395 ------------- src/python/src/grpc/_cython/.gitignore | 7 - src/python/src/grpc/_cython/README.rst | 52 -- src/python/src/grpc/_cython/__init__.py | 28 - src/python/src/grpc/_cython/_cygrpc/__init__.py | 28 - src/python/src/grpc/_cython/_cygrpc/call.pxd | 37 -- src/python/src/grpc/_cython/_cygrpc/call.pyx | 82 --- src/python/src/grpc/_cython/_cygrpc/channel.pxd | 36 -- src/python/src/grpc/_cython/_cygrpc/channel.pyx | 84 --- .../src/grpc/_cython/_cygrpc/completion_queue.pxd | 39 -- .../src/grpc/_cython/_cygrpc/completion_queue.pyx | 117 ---- .../src/grpc/_cython/_cygrpc/credentials.pxd | 45 -- .../src/grpc/_cython/_cygrpc/credentials.pyx | 207 ------- src/python/src/grpc/_cython/_cygrpc/grpc.pxd | 342 ----------- src/python/src/grpc/_cython/_cygrpc/records.pxd | 129 ----- src/python/src/grpc/_cython/_cygrpc/records.pyx | 575 ------------------ src/python/src/grpc/_cython/_cygrpc/server.pxd | 45 -- src/python/src/grpc/_cython/_cygrpc/server.pyx | 158 ----- src/python/src/grpc/_cython/adapter_low.py | 106 ---- src/python/src/grpc/_cython/adapter_low_test.py | 187 ------ src/python/src/grpc/_cython/cygrpc.pyx | 107 ---- src/python/src/grpc/_cython/cygrpc_test.py | 262 --------- src/python/src/grpc/_cython/test_utilities.py | 46 -- src/python/src/grpc/_junkdrawer/__init__.py | 30 - src/python/src/grpc/_junkdrawer/math_pb2.py | 266 --------- src/python/src/grpc/_junkdrawer/stock_pb2.py | 152 ----- src/python/src/grpc/_links/__init__.py | 30 - .../grpc/_links/_lonely_invocation_link_test.py | 88 --- src/python/src/grpc/_links/_proto_scenarios.py | 261 --------- src/python/src/grpc/_links/_transmission_test.py | 231 -------- src/python/src/grpc/_links/invocation.py | 363 ------------ src/python/src/grpc/_links/service.py | 402 ------------- src/python/src/grpc/early_adopter/__init__.py | 30 - .../src/grpc/early_adopter/implementations.py | 250 -------- .../src/grpc/early_adopter/implementations_test.py | 180 ------ src/python/src/grpc/framework/__init__.py | 30 - src/python/src/grpc/framework/alpha/__init__.py | 28 - .../src/grpc/framework/alpha/_face_utilities.py | 183 ------ src/python/src/grpc/framework/alpha/_reexport.py | 203 ------- src/python/src/grpc/framework/alpha/exceptions.py | 48 -- src/python/src/grpc/framework/alpha/interfaces.py | 388 ------------- src/python/src/grpc/framework/alpha/utilities.py | 269 --------- src/python/src/grpc/framework/base/__init__.py | 30 - .../src/grpc/framework/base/_cancellation.py | 64 --- src/python/src/grpc/framework/base/_constants.py | 32 -- src/python/src/grpc/framework/base/_context.py | 99 ---- src/python/src/grpc/framework/base/_emission.py | 125 ---- src/python/src/grpc/framework/base/_ends.py | 399 ------------- src/python/src/grpc/framework/base/_expiration.py | 158 ----- src/python/src/grpc/framework/base/_ingestion.py | 442 -------------- src/python/src/grpc/framework/base/_interfaces.py | 271 --------- src/python/src/grpc/framework/base/_reception.py | 399 ------------- src/python/src/grpc/framework/base/_termination.py | 204 ------- .../src/grpc/framework/base/_transmission.py | 429 -------------- src/python/src/grpc/framework/base/exceptions.py | 34 -- .../src/grpc/framework/base/implementations.py | 77 --- .../grpc/framework/base/implementations_test.py | 80 --- src/python/src/grpc/framework/base/in_memory.py | 108 ---- src/python/src/grpc/framework/base/interfaces.py | 363 ------------ .../grpc/framework/base/interfaces_test_case.py | 307 ---------- src/python/src/grpc/framework/base/null.py | 56 -- src/python/src/grpc/framework/base/util.py | 94 --- src/python/src/grpc/framework/common/__init__.py | 30 - .../src/grpc/framework/common/cardinality.py | 42 -- src/python/src/grpc/framework/common/style.py | 40 -- .../src/grpc/framework/common/test_constants.py | 43 -- .../src/grpc/framework/common/test_control.py | 87 --- .../src/grpc/framework/common/test_coverage.py | 116 ---- src/python/src/grpc/framework/face/__init__.py | 30 - src/python/src/grpc/framework/face/_calls.py | 422 -------------- src/python/src/grpc/framework/face/_control.py | 198 ------- src/python/src/grpc/framework/face/_service.py | 187 ------ src/python/src/grpc/framework/face/_test_case.py | 61 -- .../blocking_invocation_inline_service_test.py | 46 -- .../src/grpc/framework/face/demonstration.py | 118 ---- ...nt_invocation_synchronous_event_service_test.py | 46 -- src/python/src/grpc/framework/face/exceptions.py | 77 --- ...e_invocation_asynchronous_event_service_test.py | 46 -- .../src/grpc/framework/face/implementations.py | 318 ---------- src/python/src/grpc/framework/face/interfaces.py | 640 --------------------- .../src/grpc/framework/face/testing/__init__.py | 30 - .../src/grpc/framework/face/testing/base_util.py | 102 ---- ...blocking_invocation_inline_service_test_case.py | 222 ------- .../src/grpc/framework/face/testing/callback.py | 94 --- .../src/grpc/framework/face/testing/control.py | 87 --- .../src/grpc/framework/face/testing/coverage.py | 123 ---- .../src/grpc/framework/face/testing/digest.py | 450 --------------- ...vocation_synchronous_event_service_test_case.py | 362 ------------ ...ocation_asynchronous_event_service_test_case.py | 376 ------------ .../src/grpc/framework/face/testing/interfaces.py | 117 ---- .../src/grpc/framework/face/testing/serial.py | 70 --- .../src/grpc/framework/face/testing/service.py | 337 ----------- .../grpc/framework/face/testing/stock_service.py | 374 ------------ .../src/grpc/framework/face/testing/test_case.py | 80 --- src/python/src/grpc/framework/face/utilities.py | 177 ------ .../src/grpc/framework/foundation/__init__.py | 30 - .../src/grpc/framework/foundation/_later_test.py | 151 ----- .../framework/foundation/_logging_pool_test.py | 64 --- .../src/grpc/framework/foundation/_timer_future.py | 228 -------- .../src/grpc/framework/foundation/abandonment.py | 38 -- .../src/grpc/framework/foundation/activated.py | 65 --- .../src/grpc/framework/foundation/callable_util.py | 107 ---- src/python/src/grpc/framework/foundation/future.py | 236 -------- src/python/src/grpc/framework/foundation/later.py | 51 -- .../src/grpc/framework/foundation/logging_pool.py | 83 --- src/python/src/grpc/framework/foundation/relay.py | 175 ------ src/python/src/grpc/framework/foundation/stream.py | 60 -- .../grpc/framework/foundation/stream_testing.py | 73 --- .../src/grpc/framework/foundation/stream_util.py | 160 ------ .../src/grpc/framework/interfaces/__init__.py | 30 - .../grpc/framework/interfaces/links/__init__.py | 30 - .../src/grpc/framework/interfaces/links/links.py | 124 ---- .../grpc/framework/interfaces/links/test_cases.py | 333 ----------- .../framework/interfaces/links/test_utilities.py | 66 --- .../grpc/framework/interfaces/links/utilities.py | 44 -- src/python/src/setup.cfg | 2 - src/python/src/setup.py | 130 ----- tools/distrib/python/docgen.py | 4 +- tools/distrib/python/submit.py | 2 +- tools/run_tests/build_python.sh | 10 +- tools/run_tests/python_tests.json | 4 +- 336 files changed, 24656 insertions(+), 24675 deletions(-) create mode 100644 src/python/grpcio/.gitignore create mode 100644 src/python/grpcio/MANIFEST.in create mode 100644 src/python/grpcio/README.rst create mode 100644 src/python/grpcio/commands.py create mode 100644 src/python/grpcio/grpc/__init__.py create mode 100644 src/python/grpcio/grpc/_adapter/.gitignore create mode 100644 src/python/grpcio/grpc/_adapter/__init__.py create mode 100644 src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_c/module.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types.h create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/call.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/channel.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/server.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c create mode 100644 src/python/grpcio/grpc/_adapter/_c/utility.c create mode 100644 src/python/grpcio/grpc/_adapter/_c_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_common.py create mode 100644 src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_face_test_case.py create mode 100644 src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_intermediary_low.py create mode 100644 src/python/grpcio/grpc/_adapter/_intermediary_low_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_links_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_low.py create mode 100644 src/python/grpcio/grpc/_adapter/_low_test.py create mode 100644 src/python/grpcio/grpc/_adapter/_proto_scenarios.py create mode 100644 src/python/grpcio/grpc/_adapter/_test_links.py create mode 100644 src/python/grpcio/grpc/_adapter/_types.py create mode 100644 src/python/grpcio/grpc/_adapter/fore.py create mode 100644 src/python/grpcio/grpc/_adapter/rear.py create mode 100644 src/python/grpcio/grpc/_cython/.gitignore create mode 100644 src/python/grpcio/grpc/_cython/README.rst create mode 100644 src/python/grpcio/grpc/_cython/__init__.py create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/__init__.py create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/call.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/call.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/records.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/records.pyx create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/server.pxd create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/server.pyx create mode 100644 src/python/grpcio/grpc/_cython/adapter_low.py create mode 100644 src/python/grpcio/grpc/_cython/adapter_low_test.py create mode 100644 src/python/grpcio/grpc/_cython/cygrpc.pyx create mode 100644 src/python/grpcio/grpc/_cython/cygrpc_test.py create mode 100644 src/python/grpcio/grpc/_cython/test_utilities.py create mode 100644 src/python/grpcio/grpc/_junkdrawer/__init__.py create mode 100644 src/python/grpcio/grpc/_junkdrawer/math_pb2.py create mode 100644 src/python/grpcio/grpc/_junkdrawer/stock_pb2.py create mode 100644 src/python/grpcio/grpc/_links/__init__.py create mode 100644 src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py create mode 100644 src/python/grpcio/grpc/_links/_proto_scenarios.py create mode 100644 src/python/grpcio/grpc/_links/_transmission_test.py create mode 100644 src/python/grpcio/grpc/_links/invocation.py create mode 100644 src/python/grpcio/grpc/_links/service.py create mode 100644 src/python/grpcio/grpc/early_adopter/__init__.py create mode 100644 src/python/grpcio/grpc/early_adopter/implementations.py create mode 100644 src/python/grpcio/grpc/early_adopter/implementations_test.py create mode 100644 src/python/grpcio/grpc/framework/__init__.py create mode 100644 src/python/grpcio/grpc/framework/alpha/__init__.py create mode 100644 src/python/grpcio/grpc/framework/alpha/_face_utilities.py create mode 100644 src/python/grpcio/grpc/framework/alpha/_reexport.py create mode 100644 src/python/grpcio/grpc/framework/alpha/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/alpha/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/alpha/utilities.py create mode 100644 src/python/grpcio/grpc/framework/base/__init__.py create mode 100644 src/python/grpcio/grpc/framework/base/_cancellation.py create mode 100644 src/python/grpcio/grpc/framework/base/_constants.py create mode 100644 src/python/grpcio/grpc/framework/base/_context.py create mode 100644 src/python/grpcio/grpc/framework/base/_emission.py create mode 100644 src/python/grpcio/grpc/framework/base/_ends.py create mode 100644 src/python/grpcio/grpc/framework/base/_expiration.py create mode 100644 src/python/grpcio/grpc/framework/base/_ingestion.py create mode 100644 src/python/grpcio/grpc/framework/base/_interfaces.py create mode 100644 src/python/grpcio/grpc/framework/base/_reception.py create mode 100644 src/python/grpcio/grpc/framework/base/_termination.py create mode 100644 src/python/grpcio/grpc/framework/base/_transmission.py create mode 100644 src/python/grpcio/grpc/framework/base/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/base/implementations.py create mode 100644 src/python/grpcio/grpc/framework/base/implementations_test.py create mode 100644 src/python/grpcio/grpc/framework/base/in_memory.py create mode 100644 src/python/grpcio/grpc/framework/base/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/base/interfaces_test_case.py create mode 100644 src/python/grpcio/grpc/framework/base/null.py create mode 100644 src/python/grpcio/grpc/framework/base/util.py create mode 100644 src/python/grpcio/grpc/framework/common/__init__.py create mode 100644 src/python/grpcio/grpc/framework/common/cardinality.py create mode 100644 src/python/grpcio/grpc/framework/common/style.py create mode 100644 src/python/grpcio/grpc/framework/common/test_constants.py create mode 100644 src/python/grpcio/grpc/framework/common/test_control.py create mode 100644 src/python/grpcio/grpc/framework/common/test_coverage.py create mode 100644 src/python/grpcio/grpc/framework/face/__init__.py create mode 100644 src/python/grpcio/grpc/framework/face/_calls.py create mode 100644 src/python/grpcio/grpc/framework/face/_control.py create mode 100644 src/python/grpcio/grpc/framework/face/_service.py create mode 100644 src/python/grpcio/grpc/framework/face/_test_case.py create mode 100644 src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio/grpc/framework/face/demonstration.py create mode 100644 src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio/grpc/framework/face/exceptions.py create mode 100644 src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio/grpc/framework/face/implementations.py create mode 100644 src/python/grpcio/grpc/framework/face/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/__init__.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/base_util.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/callback.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/control.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/coverage.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/digest.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/interfaces.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/serial.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/service.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/stock_service.py create mode 100644 src/python/grpcio/grpc/framework/face/testing/test_case.py create mode 100644 src/python/grpcio/grpc/framework/face/utilities.py create mode 100644 src/python/grpcio/grpc/framework/foundation/__init__.py create mode 100644 src/python/grpcio/grpc/framework/foundation/_later_test.py create mode 100644 src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py create mode 100644 src/python/grpcio/grpc/framework/foundation/_timer_future.py create mode 100644 src/python/grpcio/grpc/framework/foundation/abandonment.py create mode 100644 src/python/grpcio/grpc/framework/foundation/activated.py create mode 100644 src/python/grpcio/grpc/framework/foundation/callable_util.py create mode 100644 src/python/grpcio/grpc/framework/foundation/future.py create mode 100644 src/python/grpcio/grpc/framework/foundation/later.py create mode 100644 src/python/grpcio/grpc/framework/foundation/logging_pool.py create mode 100644 src/python/grpcio/grpc/framework/foundation/relay.py create mode 100644 src/python/grpcio/grpc/framework/foundation/stream.py create mode 100644 src/python/grpcio/grpc/framework/foundation/stream_testing.py create mode 100644 src/python/grpcio/grpc/framework/foundation/stream_util.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/__init__.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/links.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/test_cases.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py create mode 100644 src/python/grpcio/grpc/framework/interfaces/links/utilities.py create mode 100644 src/python/grpcio/setup.cfg create mode 100644 src/python/grpcio/setup.py create mode 100644 src/python/grpcio_test/grpc_interop/__init__.py create mode 100644 src/python/grpcio_test/grpc_interop/_insecure_interop_test.py create mode 100644 src/python/grpcio_test/grpc_interop/_interop_test_case.py create mode 100644 src/python/grpcio_test/grpc_interop/_secure_interop_test.py create mode 100644 src/python/grpcio_test/grpc_interop/client.py create mode 100644 src/python/grpcio_test/grpc_interop/credentials/README create mode 100755 src/python/grpcio_test/grpc_interop/credentials/ca.pem create mode 100755 src/python/grpcio_test/grpc_interop/credentials/server1.key create mode 100755 src/python/grpcio_test/grpc_interop/credentials/server1.pem create mode 100644 src/python/grpcio_test/grpc_interop/empty_pb2.py create mode 100644 src/python/grpcio_test/grpc_interop/messages_pb2.py create mode 100644 src/python/grpcio_test/grpc_interop/methods.py create mode 100644 src/python/grpcio_test/grpc_interop/resources.py create mode 100644 src/python/grpcio_test/grpc_interop/server.py create mode 100644 src/python/grpcio_test/grpc_interop/test_pb2.py create mode 100644 src/python/grpcio_test/setup.py delete mode 100644 src/python/interop/interop/__init__.py delete mode 100644 src/python/interop/interop/_insecure_interop_test.py delete mode 100644 src/python/interop/interop/_interop_test_case.py delete mode 100644 src/python/interop/interop/_secure_interop_test.py delete mode 100644 src/python/interop/interop/client.py delete mode 100644 src/python/interop/interop/credentials/README delete mode 100755 src/python/interop/interop/credentials/ca.pem delete mode 100755 src/python/interop/interop/credentials/server1.key delete mode 100755 src/python/interop/interop/credentials/server1.pem delete mode 100644 src/python/interop/interop/empty_pb2.py delete mode 100644 src/python/interop/interop/messages_pb2.py delete mode 100644 src/python/interop/interop/methods.py delete mode 100644 src/python/interop/interop/resources.py delete mode 100644 src/python/interop/interop/server.py delete mode 100644 src/python/interop/interop/test_pb2.py delete mode 100644 src/python/interop/setup.py delete mode 100644 src/python/src/.gitignore delete mode 100644 src/python/src/MANIFEST.in delete mode 100644 src/python/src/README.rst delete mode 100644 src/python/src/commands.py delete mode 100644 src/python/src/grpc/__init__.py delete mode 100644 src/python/src/grpc/_adapter/.gitignore delete mode 100644 src/python/src/grpc/_adapter/__init__.py delete mode 100644 src/python/src/grpc/_adapter/_blocking_invocation_inline_service_test.py delete mode 100644 src/python/src/grpc/_adapter/_c/module.c delete mode 100644 src/python/src/grpc/_adapter/_c/types.c delete mode 100644 src/python/src/grpc/_adapter/_c/types.h delete mode 100644 src/python/src/grpc/_adapter/_c/types/call.c delete mode 100644 src/python/src/grpc/_adapter/_c/types/channel.c delete mode 100644 src/python/src/grpc/_adapter/_c/types/client_credentials.c delete mode 100644 src/python/src/grpc/_adapter/_c/types/completion_queue.c delete mode 100644 src/python/src/grpc/_adapter/_c/types/server.c delete mode 100644 src/python/src/grpc/_adapter/_c/types/server_credentials.c delete mode 100644 src/python/src/grpc/_adapter/_c/utility.c delete mode 100644 src/python/src/grpc/_adapter/_c_test.py delete mode 100644 src/python/src/grpc/_adapter/_common.py delete mode 100644 src/python/src/grpc/_adapter/_event_invocation_synchronous_event_service_test.py delete mode 100644 src/python/src/grpc/_adapter/_face_test_case.py delete mode 100644 src/python/src/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py delete mode 100644 src/python/src/grpc/_adapter/_intermediary_low.py delete mode 100644 src/python/src/grpc/_adapter/_intermediary_low_test.py delete mode 100644 src/python/src/grpc/_adapter/_links_test.py delete mode 100644 src/python/src/grpc/_adapter/_lonely_rear_link_test.py delete mode 100644 src/python/src/grpc/_adapter/_low.py delete mode 100644 src/python/src/grpc/_adapter/_low_test.py delete mode 100644 src/python/src/grpc/_adapter/_proto_scenarios.py delete mode 100644 src/python/src/grpc/_adapter/_test_links.py delete mode 100644 src/python/src/grpc/_adapter/_types.py delete mode 100644 src/python/src/grpc/_adapter/fore.py delete mode 100644 src/python/src/grpc/_adapter/rear.py delete mode 100644 src/python/src/grpc/_cython/.gitignore delete mode 100644 src/python/src/grpc/_cython/README.rst delete mode 100644 src/python/src/grpc/_cython/__init__.py delete mode 100644 src/python/src/grpc/_cython/_cygrpc/__init__.py delete mode 100644 src/python/src/grpc/_cython/_cygrpc/call.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/call.pyx delete mode 100644 src/python/src/grpc/_cython/_cygrpc/channel.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/channel.pyx delete mode 100644 src/python/src/grpc/_cython/_cygrpc/completion_queue.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/completion_queue.pyx delete mode 100644 src/python/src/grpc/_cython/_cygrpc/credentials.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/credentials.pyx delete mode 100644 src/python/src/grpc/_cython/_cygrpc/grpc.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/records.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/records.pyx delete mode 100644 src/python/src/grpc/_cython/_cygrpc/server.pxd delete mode 100644 src/python/src/grpc/_cython/_cygrpc/server.pyx delete mode 100644 src/python/src/grpc/_cython/adapter_low.py delete mode 100644 src/python/src/grpc/_cython/adapter_low_test.py delete mode 100644 src/python/src/grpc/_cython/cygrpc.pyx delete mode 100644 src/python/src/grpc/_cython/cygrpc_test.py delete mode 100644 src/python/src/grpc/_cython/test_utilities.py delete mode 100644 src/python/src/grpc/_junkdrawer/__init__.py delete mode 100644 src/python/src/grpc/_junkdrawer/math_pb2.py delete mode 100644 src/python/src/grpc/_junkdrawer/stock_pb2.py delete mode 100644 src/python/src/grpc/_links/__init__.py delete mode 100644 src/python/src/grpc/_links/_lonely_invocation_link_test.py delete mode 100644 src/python/src/grpc/_links/_proto_scenarios.py delete mode 100644 src/python/src/grpc/_links/_transmission_test.py delete mode 100644 src/python/src/grpc/_links/invocation.py delete mode 100644 src/python/src/grpc/_links/service.py delete mode 100644 src/python/src/grpc/early_adopter/__init__.py delete mode 100644 src/python/src/grpc/early_adopter/implementations.py delete mode 100644 src/python/src/grpc/early_adopter/implementations_test.py delete mode 100644 src/python/src/grpc/framework/__init__.py delete mode 100644 src/python/src/grpc/framework/alpha/__init__.py delete mode 100644 src/python/src/grpc/framework/alpha/_face_utilities.py delete mode 100644 src/python/src/grpc/framework/alpha/_reexport.py delete mode 100644 src/python/src/grpc/framework/alpha/exceptions.py delete mode 100644 src/python/src/grpc/framework/alpha/interfaces.py delete mode 100644 src/python/src/grpc/framework/alpha/utilities.py delete mode 100644 src/python/src/grpc/framework/base/__init__.py delete mode 100644 src/python/src/grpc/framework/base/_cancellation.py delete mode 100644 src/python/src/grpc/framework/base/_constants.py delete mode 100644 src/python/src/grpc/framework/base/_context.py delete mode 100644 src/python/src/grpc/framework/base/_emission.py delete mode 100644 src/python/src/grpc/framework/base/_ends.py delete mode 100644 src/python/src/grpc/framework/base/_expiration.py delete mode 100644 src/python/src/grpc/framework/base/_ingestion.py delete mode 100644 src/python/src/grpc/framework/base/_interfaces.py delete mode 100644 src/python/src/grpc/framework/base/_reception.py delete mode 100644 src/python/src/grpc/framework/base/_termination.py delete mode 100644 src/python/src/grpc/framework/base/_transmission.py delete mode 100644 src/python/src/grpc/framework/base/exceptions.py delete mode 100644 src/python/src/grpc/framework/base/implementations.py delete mode 100644 src/python/src/grpc/framework/base/implementations_test.py delete mode 100644 src/python/src/grpc/framework/base/in_memory.py delete mode 100644 src/python/src/grpc/framework/base/interfaces.py delete mode 100644 src/python/src/grpc/framework/base/interfaces_test_case.py delete mode 100644 src/python/src/grpc/framework/base/null.py delete mode 100644 src/python/src/grpc/framework/base/util.py delete mode 100644 src/python/src/grpc/framework/common/__init__.py delete mode 100644 src/python/src/grpc/framework/common/cardinality.py delete mode 100644 src/python/src/grpc/framework/common/style.py delete mode 100644 src/python/src/grpc/framework/common/test_constants.py delete mode 100644 src/python/src/grpc/framework/common/test_control.py delete mode 100644 src/python/src/grpc/framework/common/test_coverage.py delete mode 100644 src/python/src/grpc/framework/face/__init__.py delete mode 100644 src/python/src/grpc/framework/face/_calls.py delete mode 100644 src/python/src/grpc/framework/face/_control.py delete mode 100644 src/python/src/grpc/framework/face/_service.py delete mode 100644 src/python/src/grpc/framework/face/_test_case.py delete mode 100644 src/python/src/grpc/framework/face/blocking_invocation_inline_service_test.py delete mode 100644 src/python/src/grpc/framework/face/demonstration.py delete mode 100644 src/python/src/grpc/framework/face/event_invocation_synchronous_event_service_test.py delete mode 100644 src/python/src/grpc/framework/face/exceptions.py delete mode 100644 src/python/src/grpc/framework/face/future_invocation_asynchronous_event_service_test.py delete mode 100644 src/python/src/grpc/framework/face/implementations.py delete mode 100644 src/python/src/grpc/framework/face/interfaces.py delete mode 100644 src/python/src/grpc/framework/face/testing/__init__.py delete mode 100644 src/python/src/grpc/framework/face/testing/base_util.py delete mode 100644 src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py delete mode 100644 src/python/src/grpc/framework/face/testing/callback.py delete mode 100644 src/python/src/grpc/framework/face/testing/control.py delete mode 100644 src/python/src/grpc/framework/face/testing/coverage.py delete mode 100644 src/python/src/grpc/framework/face/testing/digest.py delete mode 100644 src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py delete mode 100644 src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py delete mode 100644 src/python/src/grpc/framework/face/testing/interfaces.py delete mode 100644 src/python/src/grpc/framework/face/testing/serial.py delete mode 100644 src/python/src/grpc/framework/face/testing/service.py delete mode 100644 src/python/src/grpc/framework/face/testing/stock_service.py delete mode 100644 src/python/src/grpc/framework/face/testing/test_case.py delete mode 100644 src/python/src/grpc/framework/face/utilities.py delete mode 100644 src/python/src/grpc/framework/foundation/__init__.py delete mode 100644 src/python/src/grpc/framework/foundation/_later_test.py delete mode 100644 src/python/src/grpc/framework/foundation/_logging_pool_test.py delete mode 100644 src/python/src/grpc/framework/foundation/_timer_future.py delete mode 100644 src/python/src/grpc/framework/foundation/abandonment.py delete mode 100644 src/python/src/grpc/framework/foundation/activated.py delete mode 100644 src/python/src/grpc/framework/foundation/callable_util.py delete mode 100644 src/python/src/grpc/framework/foundation/future.py delete mode 100644 src/python/src/grpc/framework/foundation/later.py delete mode 100644 src/python/src/grpc/framework/foundation/logging_pool.py delete mode 100644 src/python/src/grpc/framework/foundation/relay.py delete mode 100644 src/python/src/grpc/framework/foundation/stream.py delete mode 100644 src/python/src/grpc/framework/foundation/stream_testing.py delete mode 100644 src/python/src/grpc/framework/foundation/stream_util.py delete mode 100644 src/python/src/grpc/framework/interfaces/__init__.py delete mode 100644 src/python/src/grpc/framework/interfaces/links/__init__.py delete mode 100644 src/python/src/grpc/framework/interfaces/links/links.py delete mode 100644 src/python/src/grpc/framework/interfaces/links/test_cases.py delete mode 100644 src/python/src/grpc/framework/interfaces/links/test_utilities.py delete mode 100644 src/python/src/grpc/framework/interfaces/links/utilities.py delete mode 100644 src/python/src/setup.cfg delete mode 100644 src/python/src/setup.py (limited to 'src/python') diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore new file mode 100644 index 0000000000..d89f3db999 --- /dev/null +++ b/src/python/grpcio/.gitignore @@ -0,0 +1,8 @@ +MANIFEST +grpcio.egg-info/ +build/ +dist/ +*.egg +*.egg/ +*.eggs/ +doc/ diff --git a/src/python/grpcio/MANIFEST.in b/src/python/grpcio/MANIFEST.in new file mode 100644 index 0000000000..498b55f20a --- /dev/null +++ b/src/python/grpcio/MANIFEST.in @@ -0,0 +1,2 @@ +graft grpc +include commands.py diff --git a/src/python/grpcio/README.rst b/src/python/grpcio/README.rst new file mode 100644 index 0000000000..00bdecf56f --- /dev/null +++ b/src/python/grpcio/README.rst @@ -0,0 +1,23 @@ +gRPC Python +=========== + +Package for GRPC Python. + +Dependencies +------------ + +Ensure you have installed the gRPC core. On Mac OS X, install homebrew_. On Linux, install linuxbrew_. +Run the following command to install gRPC Python. + +:: + + $ curl -fsSL https://goo.gl/getgrpc | bash -s python + +This will download and run the [gRPC install script][] to install grpc core. The script then uses pip to install this package. It also installs the Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for python. + +Otherwise, `install from source`_ + +.. _`install from source`: https://github.com/grpc/grpc/blob/master/src/python/README.md#building-from-source +.. _homebrew: http://brew.sh +.. _linuxbrew: https://github.com/Homebrew/linuxbrew#installation +.. _`gRPC install script`: https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py new file mode 100644 index 0000000000..605d9d5612 --- /dev/null +++ b/src/python/grpcio/commands.py @@ -0,0 +1,76 @@ +# 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. + +"""Provides distutils command classes for the GRPC Python setup process.""" + +import os +import os.path +import sys + +import setuptools + +_CONF_PY_ADDENDUM = """ +extensions.append('sphinx.ext.napoleon') +napoleon_google_docstring = True +napoleon_numpy_docstring = True + +html_theme = 'sphinx_rtd_theme' +""" + + +class SphinxDocumentation(setuptools.Command): + """Command to generate documentation via sphinx.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # We import here to ensure that setup.py has had a chance to install the + # relevant package eggs first. + import sphinx + import sphinx.apidoc + metadata = self.distribution.metadata + src_dir = os.path.join( + os.getcwd(), self.distribution.package_dir['grpc']) + sys.path.append(src_dir) + sphinx.apidoc.main([ + '', '--force', '--full', '-H', metadata.name, '-A', metadata.author, + '-V', metadata.version, '-R', metadata.version, + '-o', os.path.join('doc', 'src'), src_dir]) + conf_filepath = os.path.join('doc', 'src', 'conf.py') + with open(conf_filepath, 'a') as conf_file: + conf_file.write(_CONF_PY_ADDENDUM) + sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) + diff --git a/src/python/grpcio/grpc/__init__.py b/src/python/grpcio/grpc/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/_adapter/.gitignore b/src/python/grpcio/grpc/_adapter/.gitignore new file mode 100644 index 0000000000..a6f96cd6db --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/.gitignore @@ -0,0 +1,5 @@ +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio/grpc/_adapter/__init__.py b/src/python/grpcio/grpc/_adapter/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py b/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py new file mode 100644 index 0000000000..7a8ff0ad89 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc._adapter import _face_test_case +from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _face_test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_c/module.c b/src/python/grpcio/grpc/_adapter/_c/module.c new file mode 100644 index 0000000000..1f3aedd9d8 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/module.c @@ -0,0 +1,61 @@ +/* + * + * 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 + +#define PY_SSIZE_T_CLEAN +#include +#include + +#include "grpc/_adapter/_c/types.h" + +static PyMethodDef c_methods[] = { + {NULL} +}; + +PyMODINIT_FUNC init_c(void) { + PyObject *module; + + module = Py_InitModule3("_c", c_methods, + "Wrappings of C structures and functions."); + + 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); +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types.c b/src/python/grpcio/grpc/_adapter/_c/types.c new file mode 100644 index 0000000000..8855c32ca6 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types.c @@ -0,0 +1,60 @@ +/* + * + * 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 +#include + +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; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types.h b/src/python/grpcio/grpc/_adapter/_c/types.h new file mode 100644 index 0000000000..4e0da4a28a --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types.h @@ -0,0 +1,267 @@ +/* + * + * 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 +#include +#include + + +/*=========================*/ +/* 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_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); +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_pyseq_to_send_metadata( + PyObject *pyseq, grpc_metadata **metadata, size_t *count); +/* Returns a metadata array as a Python object on success, else NULL. */ +PyObject *pygrpc_cast_metadata_array_to_pyseq(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_ */ diff --git a/src/python/grpcio/grpc/_adapter/_c/types/call.c b/src/python/grpcio/grpc/_adapter/_c/types/call.c new file mode 100644 index 0000000000..0739070044 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/call.c @@ -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 +#include +#include + + +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); +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c new file mode 100644 index 0000000000..feb256cf00 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/channel.c @@ -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 +#include + + +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_insecure_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; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c new file mode 100644 index 0000000000..e314c15324 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/client_credentials.c @@ -0,0 +1,270 @@ +/* + * + * 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 +#include +#include + + +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, ""}, + {"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; +} + +/* TODO: Rename this credentials to something like service_account_jwt_access */ +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_service_account_jwt_access_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_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; +} + diff --git a/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c b/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c new file mode 100644 index 0000000000..2dd44b6ddd --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c @@ -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 +#include + + +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; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server.c b/src/python/grpcio/grpc/_adapter/_c/types/server.c new file mode 100644 index 0000000000..2a00f34039 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/server.c @@ -0,0 +1,180 @@ +/* + * + * 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 +#include + + +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); + grpc_server_register_completion_queue(self->c_serv, cq->c_cq); + 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; + pygrpc_tag *tag; + static char *keywords[] = {"tag", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) { + return NULL; + } + tag = pygrpc_produce_server_shutdown_tag(user_tag); + grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag); + Py_RETURN_NONE; +} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c new file mode 100644 index 0000000000..f6859b79d7 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c @@ -0,0 +1,138 @@ +/* + * + * 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 +#include +#include +#include + + +PyMethodDef pygrpc_ServerCredentials_methods[] = { + {"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl, + METH_CLASS|METH_KEYWORDS, ""}, + {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); + /* TODO: Add a force_client_auth parameter in the python object and pass it + here as the last arg. */ + self->c_creds = grpc_ssl_server_credentials_create( + root_certs, key_cert_pairs, num_key_cert_pairs, 0); + gpr_free(key_cert_pairs); + return self; +} + diff --git a/src/python/grpcio/grpc/_adapter/_c/utility.c b/src/python/grpcio/grpc/_adapter/_c/utility.c new file mode 100644 index 0000000000..51f3c9be01 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c/utility.c @@ -0,0 +1,506 @@ +/* + * + * 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 +#include + +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include +#include +#include + +#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_pyseq(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 ? (PyObject*)tag->call : Py_None, 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; + int type; + Py_ssize_t message_size; + char *message; + char *status_details; + gpr_slice message_slice; + 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; + gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE); + PyErr_SetString(PyExc_ValueError, buf); + gpr_free(buf); + return 0; + } + type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); + if (PyErr_Occurred()) { + return 0; + } + c_op.op = type; + c_op.flags = 0; + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!pygrpc_cast_pyseq_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_raw_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_pyseq_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; + gpr_asprintf(&buf, "expected tuple status in op of length %d", + STATUS_TUPLE_SIZE); + PyErr_SetString(PyExc_ValueError, buf); + gpr_free(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) { + size_t i; + switch(op.op) { + case GRPC_OP_SEND_INITIAL_METADATA: + /* Whenever we produce send-metadata, we allocate new strings (to handle + arbitrary sequence input as opposed to just lists or just tuples). We + thus must free those elements. */ + for (i = 0; i < op.data.send_initial_metadata.count; ++i) { + gpr_free((void *)op.data.send_initial_metadata.metadata[i].key); + gpr_free((void *)op.data.send_initial_metadata.metadata[i].value); + } + 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: + /* Whenever we produce send-metadata, we allocate new strings (to handle + arbitrary sequence input as opposed to just lists or just tuples). We + thus must free those elements. */ + for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count; + ++i) { + gpr_free( + (void *)op.data.send_status_from_server.trailing_metadata[i].key); + gpr_free( + (void *)op.data.send_status_from_server.trailing_metadata[i].value); + } + 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) { + timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); + return timespec.tv_sec + 1e-9*timespec.tv_nsec; +} + +/* Because C89 doesn't have a way to check for infinity... */ +static int pygrpc_isinf(double x) { + return x * 0 != 0; +} + +gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { + gpr_timespec result; + if (pygrpc_isinf(seconds)) { + result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) + : gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + result.tv_sec = (time_t)seconds; + result.tv_nsec = ((seconds - result.tv_sec) * 1e9); + result.clock_type = GPR_CLOCK_REALTIME; + } + 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; + args.num_args = num_args; + args.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_pyseq_to_send_metadata( + PyObject *pyseq, grpc_metadata **metadata, size_t *count) { + size_t i; + Py_ssize_t value_length; + char *key; + char *value; + if (!PySequence_Check(pyseq)) { + return 0; + } + *count = PySequence_Size(pyseq); + *metadata = gpr_malloc(sizeof(grpc_metadata) * *count); + for (i = 0; i < *count; ++i) { + PyObject *item = PySequence_GetItem(pyseq, i); + if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) { + Py_DECREF(item); + gpr_free(*metadata); + *count = 0; + *metadata = NULL; + return 0; + } else { + (*metadata)[i].key = gpr_strdup(key); + (*metadata)[i].value = gpr_malloc(value_length); + memcpy((void *)(*metadata)[i].value, value, value_length); + Py_DECREF(item); + } + (*metadata)[i].value_length = value_length; + } + return 1; +} + +PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { + PyObject *result = PyTuple_New(metadata.count); + size_t i; + for (i = 0; i < metadata.count; ++i) { + PyTuple_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; + gpr_slice slice; + char *read_result = NULL; + size_t size = 0; + grpc_byte_buffer_reader_init(&reader, buffer); + 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); + } + *result_size = size; + *result = read_result; +} diff --git a/src/python/grpcio/grpc/_adapter/_c_test.py b/src/python/grpcio/grpc/_adapter/_c_test.py new file mode 100644 index 0000000000..fe020e2a9c --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_c_test.py @@ -0,0 +1,55 @@ +# 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 time +import unittest + +from grpc._adapter import _c +from grpc._adapter import _types + + +class CTypeSmokeTest(unittest.TestCase): + + def testCompletionQueueUpDown(self): + completion_queue = _c.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + completion_queue = _c.CompletionQueue() + serv = _c.Server(completion_queue, []) + del serv + del completion_queue + + def testChannelUpDown(self): + channel = _c.Channel('[::]:0', []) + del channel + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_common.py b/src/python/grpcio/grpc/_adapter/_common.py new file mode 100644 index 0000000000..492849f4cb --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_common.py @@ -0,0 +1,76 @@ +# 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. + +"""State used by both invocation-side and service-side code.""" + +import enum + + +@enum.unique +class HighWrite(enum.Enum): + """The possible categories of high-level write state.""" + + OPEN = 'OPEN' + CLOSED = 'CLOSED' + + +class WriteState(object): + """A description of the state of writing to an RPC. + + Attributes: + low: A side-specific value describing the low-level state of writing. + high: A HighWrite value describing the high-level state of writing. + pending: A list of bytestrings for the RPC waiting to be written to the + other side of the RPC. + """ + + def __init__(self, low, high, pending): + self.low = low + self.high = high + self.pending = pending + + +class CommonRPCState(object): + """A description of an RPC's state. + + Attributes: + write: A WriteState describing the state of writing to the RPC. + sequence_number: The lowest-unused sequence number for use in generating + tickets locally describing the progress of the RPC. + deserializer: The behavior to be used to deserialize payload bytestreams + taken off the wire. + serializer: The behavior to be used to serialize payloads to be sent on the + wire. + """ + + def __init__(self, write, sequence_number, deserializer, serializer): + self.write = write + self.sequence_number = sequence_number + self.deserializer = deserializer + self.serializer = serializer diff --git a/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py new file mode 100644 index 0000000000..b8ceb75d68 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc._adapter import _face_test_case +from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_face_test_case.py b/src/python/grpcio/grpc/_adapter/_face_test_case.py new file mode 100644 index 0000000000..5fa974ed06 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_face_test_case.py @@ -0,0 +1,106 @@ +# 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. + +"""Common construction and destruction for GRPC-backed Face-layer tests.""" + +import unittest + +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import util +from grpc.framework.base import implementations as base_implementations +from grpc.framework.face import implementations as face_implementations +from grpc.framework.face.testing import coverage +from grpc.framework.face.testing import serial +from grpc.framework.face.testing import test_case +from grpc.framework.foundation import logging_pool + +_TIMEOUT = 3 +_MAXIMUM_TIMEOUT = 90 +_MAXIMUM_POOL_SIZE = 4 + + +class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): + """Provides abstract Face-layer tests a GRPC-backed implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = face_implementations.servicer( + pool, method_implementations, multi_method_implementation) + + serialization = serial.serialization(methods) + + fore_link = fore.ForeLink( + pool, serialization.request_deserializers, + serialization.response_serializers, None, ()) + fore_link.start() + port = fore_link.port() + rear_link = rear.RearLink( + 'localhost', port, pool, + serialization.request_serializers, + serialization.response_deserializers, False, None, None, None) + rear_link.start() + front = base_implementations.front_link(pool, pool, pool) + back = base_implementations.back_link( + servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) + fore_link.join_rear_link(back) + back.join_fore_link(fore_link) + rear_link.join_fore_link(front) + front.join_rear_link(rear_link) + + stub = face_implementations.generic_stub(front, pool) + return stub, (rear_link, fore_link, front, back) + + def tear_down_implementation(self, memo): + rear_link, fore_link, front, back = memo + # TODO(nathaniel): Waiting for the front and back to idle possibly should + # not be necessary - investigate as part of graceful shutdown work. + util.wait_for_idle(front) + util.wait_for_idle(back) + rear_link.stop() + fore_link.stop() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py new file mode 100644 index 0000000000..3773e65575 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc._adapter import _face_test_case +from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low.py b/src/python/grpcio/grpc/_adapter/_intermediary_low.py new file mode 100644 index 0000000000..3c7f0a2619 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_intermediary_low.py @@ -0,0 +1,259 @@ +# 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): + result = self._internal.start_batch([ + _types.OpArgs.send_initial_metadata(self._metadata) + ], _IGNORE_ME_TAG) + self._metadata = [] + return result + + 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 len(ev.results) > 0 and 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(_TagAdapter(None, Event.Kind.STOP)) + + +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)) diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py b/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py new file mode 100644 index 0000000000..27a5b82e9c --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py @@ -0,0 +1,434 @@ +# 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 Queue +import threading +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 + + +def _drive_completion_queue(completion_queue, event_queue): + while True: + event = completion_queue.get(_FUTURE) + if event.kind is _low.Event.Kind.STOP: + break + event_queue.put(event) + + +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.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + server_terminal_event_two = self.server_events.get() + 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_events.get() + client_terminal_event_two = self.client_events.get() + 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]) + self.assertSetEqual(set(key for key, _ in finish_accepted.metadata), + set((server_trailing_metadata_key, + 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.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + 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_events.get() + server_call = service_accepted.service_acceptance.call + + server_call.accept(self.server_completion_queue, finish_tag) + server_call.premetadata() + + metadata_accepted = self.client_events.get() + self.assertIsNotNone(metadata_accepted) + + for datum in test_data: + client_call.write(datum, write_tag) + write_accepted = self.client_events.get() + + server_call.read(read_tag) + read_accepted = self.server_events.get() + server_data.append(read_accepted.bytes) + + server_call.write(read_accepted.bytes, write_tag) + write_accepted = self.server_events.get() + self.assertIsNotNone(write_accepted) + + client_call.read(read_tag) + read_accepted = self.client_events.get() + 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_events.get() + server_terminal_event_two = self.server_events.get() + 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_events.get() + 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) + diff --git a/src/python/grpcio/grpc/_adapter/_links_test.py b/src/python/grpcio/grpc/_adapter/_links_test.py new file mode 100644 index 0000000000..4729b84f84 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_links_test.py @@ -0,0 +1,277 @@ +# 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. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import threading +import unittest + +from grpc._adapter import _proto_scenarios +from grpc._adapter import _test_links +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool + +_IDENTITY = lambda x: x +_TIMEOUT = 32 + + +# TODO(nathaniel): End-to-end metadata testing. +def _transform_metadata(unused_metadata): + return ( + ('one unused key', 'one unused value'), + ('another unused key', 'another unused value'), +) + + +class RoundTripTest(unittest.TestCase): + + def setUp(self): + self.fore_link_pool = logging_pool.pool(8) + self.rear_link_pool = logging_pool.pool(8) + + def tearDown(self): + self.rear_link_pool.shutdown(wait=True) + self.fore_link_pool.shutdown(wait=True) + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_fore_link = _test_links.ForeLink(None, None) + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE): + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, 0, + interfaces.BackToFrontTicket.Kind.COMPLETION, None) + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: None}, + {test_method: None}, False, None, None, None, + metadata_transformer=_transform_metadata) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is + interfaces.BackToFrontTicket.Kind.CONTINUATION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_fore_link.condition: + self.assertIs( + test_fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testEntireRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_front_to_back_datum = b'\x07' + test_back_to_front_datum = b'\x08' + test_fore_link = _test_links.ForeLink(None, None) + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.payload is None: + payload = None + else: + payload = test_back_to_front_datum + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if payload is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + payload) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, + test_front_to_back_datum, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + front_to_back_payloads = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + back_to_front_payloads = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) + self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_method = scenario.method() + test_fore_link = _test_links.ForeLink(None, None) + rear_lock = threading.Lock() + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + with rear_lock: + if front_to_back_ticket.payload is not None: + response = scenario.response_for_request(front_to_back_ticket.payload) + else: + response = None + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if response is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + response) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: scenario.deserialize_request}, + {test_method: scenario.serialize_response}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, + {test_method: scenario.serialize_request}, + {test_method: scenario.deserialize_response}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + commencement_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, + interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + fore_sequence_number = 1 + rear_link.accept_front_to_back_ticket(commencement_ticket) + for request in scenario.requests(): + continuation_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, + request, None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(continuation_ticket) + completion_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, + None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(completion_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + requests = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + responses = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(requests)) + self.assertTrue(scenario.verify_responses(responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py b/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py new file mode 100644 index 0000000000..7f5021f40e --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py @@ -0,0 +1,100 @@ +# 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import _test_links +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool + +_IDENTITY = lambda x: x +_TIMEOUT = 2 + + +class LonelyRearLinkTest(unittest.TestCase): + + def setUp(self): + self.pool = logging_pool.pool(8) + + def tearDown(self): + self.pool.shutdown(wait=True) + + def testUpAndDown(self): + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) + + rear_link.start() + rear_link.stop() + + def _perform_lonely_client_test_with_ticket_kind( + self, front_to_back_ticket_kind): + test_operation_id = object() + test_method = 'test method' + fore_link = _test_links.ForeLink(None, None) + + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {test_method: None}, + {test_method: None}, False, None, None, None) + rear_link.join_fore_link(fore_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, front_to_back_ticket_kind, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with fore_link.condition: + while True: + if (fore_link.tickets and + fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.CONTINUATION): + break + fore_link.condition.wait() + + rear_link.stop() + + with fore_link.condition: + self.assertIsNot( + fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testLonelyClientCommencementTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.COMMENCEMENT) + + def testLonelyClientEntireTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.ENTIRE) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_low.py b/src/python/grpcio/grpc/_adapter/_low.py new file mode 100644 index 0000000000..dcf67dbc11 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_low.py @@ -0,0 +1,108 @@ +# 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. + +from grpc._adapter import _c +from grpc._adapter import _types + +ClientCredentials = _c.ClientCredentials +ServerCredentials = _c.ServerCredentials + + +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=None): + return self.server.shutdown(tag) + + def request_call(self, completion_queue, tag): + return self.server.request_call(completion_queue.completion_queue, tag) diff --git a/src/python/grpcio/grpc/_adapter/_low_test.py b/src/python/grpcio/grpc/_adapter/_low_test.py new file mode 100644 index 0000000000..9a8edfad0c --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_low_test.py @@ -0,0 +1,199 @@ +# 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 threading +import time +import unittest + +from grpc._adapter import _types +from grpc._adapter import _low + + +def WaitForEvents(completion_queues, deadline): + """ + Args: + completion_queues: list of completion queues to wait for events on + deadline: absolute deadline to wait until + + Returns: + a sequence of events of length len(completion_queues). + """ + + results = [None] * len(completion_queues) + lock = threading.Lock() + threads = [] + def set_ith_result(i, completion_queue): + result = completion_queue.next(deadline) + with lock: + print i, completion_queue, result, time.time() - deadline + results[i] = result + for i, completion_queue in enumerate(completion_queues): + thread = threading.Thread(target=set_ith_result, + args=[i, completion_queue]) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + return results + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + 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 + del self.server + + 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) + + client_no_event, request_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 2) + self.assertEquals(client_no_event, None) + 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)) + got_initial_metadata = dict(request_event.results[0].initial_metadata) + self.assertEquals( + dict(client_initial_metadata), + dict((x, got_initial_metadata[x]) for x in zip(*client_initial_metadata)[0])) + 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, server_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 1) + + 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__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_proto_scenarios.py b/src/python/grpcio/grpc/_adapter/_proto_scenarios.py new file mode 100644 index 0000000000..60a622ba8b --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_proto_scenarios.py @@ -0,0 +1,261 @@ +# 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc._junkdrawer import math_pb2 + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def method(self): + """Access the test method name. + + Returns: + The test method name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def method(self): + return 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _STREAM_LENGTH = 200 + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(_STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio/grpc/_adapter/_test_links.py b/src/python/grpcio/grpc/_adapter/_test_links.py new file mode 100644 index 0000000000..86c7e61b17 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_test_links.py @@ -0,0 +1,80 @@ +# 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. + +"""Links suitable for use in tests.""" + +import threading + +from grpc.framework.base import interfaces + + +class ForeLink(interfaces.ForeLink): + """A ForeLink suitable for use in tests of RearLinks.""" + + def __init__(self, action, rear_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.rear_link = rear_link + + def accept_back_to_front_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, rear_link = self.action, self.rear_link + + if action is not None: + action(ticket, rear_link) + + def join_rear_link(self, rear_link): + with self.condition: + self.rear_link = rear_link + + +class RearLink(interfaces.RearLink): + """A RearLink suitable for use in tests of ForeLinks.""" + + def __init__(self, action, fore_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.fore_link = fore_link + + def accept_front_to_back_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, fore_link = self.action, self.fore_link + + if action is not None: + action(ticket, fore_link) + + def join_fore_link(self, fore_link): + with self.condition: + self.fore_link = fore_link diff --git a/src/python/grpcio/grpc/_adapter/_types.py b/src/python/grpcio/grpc/_adapter/_types.py new file mode 100644 index 0000000000..5ddb1774ea --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/_types.py @@ -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) ... + """ diff --git a/src/python/grpcio/grpc/_adapter/fore.py b/src/python/grpcio/grpc/_adapter/fore.py new file mode 100644 index 0000000000..7d88bda263 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/fore.py @@ -0,0 +1,363 @@ +# 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. + +"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import enum +import logging +import threading +import time + +from grpc._adapter import _common +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 +from grpc.framework.foundation import logging_pool + +_THREAD_POOL_SIZE = 10 + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +def _write(call, rpc_state, payload): + serialized_payload = rpc_state.serializer(payload) + if rpc_state.write.low is _LowWrite.OPEN: + call.write(serialized_payload, call) + rpc_state.write.low = _LowWrite.ACTIVE + else: + rpc_state.write.pending.append(serialized_payload) + + +def _status(call, rpc_state): + call.status(_low.Status(_low.Code.OK, ''), call) + rpc_state.write.low = _LowWrite.CLOSED + + +class ForeLink(base_interfaces.ForeLink, activated.Activated): + """A service-side bridge between RPC Framework and the C-ish _low code.""" + + def __init__( + self, pool, request_deserializers, response_serializers, + root_certificates, key_chain_pairs, port=None): + """Constructor. + + Args: + pool: A thread pool. + request_deserializers: A dict from RPC method names to request object + deserializer behaviors. + response_serializers: A dict from RPC method names to response object + serializer behaviors. + root_certificates: The PEM-encoded client root certificates as a + bytestring or None. + key_chain_pairs: A sequence of PEM-encoded private key-certificate chain + pairs. + port: The port on which to serve, or None to have a port selected + automatically. + """ + self._condition = threading.Condition() + self._pool = pool + self._request_deserializers = request_deserializers + self._response_serializers = response_serializers + self._root_certificates = root_certificates + self._key_chain_pairs = key_chain_pairs + self._requested_port = port + + self._rear_link = null.NULL_REAR_LINK + self._completion_queue = None + self._server = None + self._rpc_states = {} + self._spinning = False + self._port = None + + def _on_stop_event(self): + self._spinning = False + self._condition.notify_all() + + def _on_service_acceptance_event(self, event, server): + """Handle a service invocation event.""" + service_acceptance = event.service_acceptance + if service_acceptance is None: + return + + call = service_acceptance.call + call.accept(self._completion_queue, call) + # TODO(nathaniel): Metadata support. + call.premetadata() + call.read(call) + method = service_acceptance.method + + self._rpc_states[call] = _common.CommonRPCState( + _common.WriteState(_LowWrite.OPEN, _common.HighWrite.OPEN, []), 1, + self._request_deserializers[method], + self._response_serializers[method]) + + ticket = base_interfaces.FrontToBackTicket( + call, 0, base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT, method, + base_interfaces.ServicedSubscription.Kind.FULL, None, None, + service_acceptance.deadline - time.time()) + self._rear_link.accept_front_to_back_ticket(ticket) + + server.service(None) + + def _on_read_event(self, event): + """Handle data arriving during an RPC.""" + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + if event.bytes is None: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, + None, None) + else: + call.read(call) + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, + None, rpc_state.deserializer(event.bytes), None) + + self._rear_link.accept_front_to_back_ticket(ticket) + + def _on_write_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.write.pending: + serialized_payload = rpc_state.write.pending.pop(0) + call.write(serialized_payload, call) + elif rpc_state.write.high is _common.HighWrite.CLOSED: + _status(call, rpc_state) + else: + rpc_state.write.low = _LowWrite.OPEN + + def _on_complete_event(self, event): + if not event.complete_accepted: + logging.error('Complete not accepted! %s', (event,)) + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, + None, None, None, None) + self._rear_link.accept_front_to_back_ticket(ticket) + + def _on_finish_event(self, event): + """Handle termination of an RPC.""" + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + + code = event.status.code + if code is _low.Code.OK: + return + + sequence_number = rpc_state.sequence_number + rpc_state.sequence_number += 1 + if code is _low.Code.CANCELLED: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None, + None, None, None) + elif code is _low.Code.DEADLINE_EXCEEDED: + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None, + None, None) + else: + # TODO(nathaniel): Better mapping of codes to ticket-categories + ticket = base_interfaces.FrontToBackTicket( + call, sequence_number, + base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, + None, None, None, None) + self._rear_link.accept_front_to_back_ticket(ticket) + + def _spin(self, completion_queue, server): + while True: + event = completion_queue.get(None) + + with self._condition: + if event.kind is _low.Event.Kind.STOP: + self._on_stop_event() + return + elif self._server is None: + continue + elif event.kind is _low.Event.Kind.SERVICE_ACCEPTED: + self._on_service_acceptance_event(event, server) + elif event.kind is _low.Event.Kind.READ_ACCEPTED: + self._on_read_event(event) + elif event.kind is _low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(event) + elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: + self._on_complete_event(event) + elif event.kind is _low.Event.Kind.FINISH: + self._on_finish_event(event) + else: + logging.error('Illegal event! %s', (event,)) + + def _continue(self, call, payload): + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + _write(call, rpc_state, payload) + + def _complete(self, call, payload): + """Handle completion of the writes of an RPC.""" + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.write.low is _LowWrite.OPEN: + if payload is None: + _status(call, rpc_state) + else: + _write(call, rpc_state, payload) + elif rpc_state.write.low is _LowWrite.ACTIVE: + if payload is not None: + rpc_state.write.pending.append(rpc_state.serializer(payload)) + else: + raise ValueError('Called to complete after having already completed!') + rpc_state.write.high = _common.HighWrite.CLOSED + + def _cancel(self, call): + call.cancel() + self._rpc_states.pop(call, None) + + def join_rear_link(self, rear_link): + """See base_interfaces.ForeLink.join_rear_link for specification.""" + self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link + + def _start(self): + """Starts this ForeLink. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._condition: + address = '[::]:%d' % ( + 0 if self._requested_port is None else self._requested_port) + self._completion_queue = _low.CompletionQueue() + if self._root_certificates is None and not self._key_chain_pairs: + self._server = _low.Server(self._completion_queue) + self._port = self._server.add_http2_addr(address) + else: + server_credentials = _low.ServerCredentials( + self._root_certificates, self._key_chain_pairs) + self._server = _low.Server(self._completion_queue) + self._port = self._server.add_secure_http2_addr( + address, server_credentials) + self._server.start() + + self._server.service(None) + + self._pool.submit(self._spin, self._completion_queue, self._server) + self._spinning = True + + return self + + # TODO(nathaniel): Expose graceful-shutdown semantics in which this object + # enters a state in which it finishes ongoing RPCs but refuses new ones. + def _stop(self): + """Stops this ForeLink. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._condition: + self._server.stop() + # TODO(nathaniel): Yep, this is weird. Deleting a server shouldn't have a + # behaviorally significant side-effect. + self._server = None + self._completion_queue.stop() + + while self._spinning: + self._condition.wait() + + self._port = None + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + def port(self): + """Identifies the port on which this ForeLink is servicing RPCs. + + Returns: + The number of the port on which this ForeLink is servicing RPCs, or None + if this ForeLink is not currently activated and servicing RPCs. + """ + with self._condition: + return self._port + + def accept_back_to_front_ticket(self, ticket): + """See base_interfaces.ForeLink.accept_back_to_front_ticket for spec.""" + with self._condition: + if self._server is None: + return + + if ticket.kind is base_interfaces.BackToFrontTicket.Kind.CONTINUATION: + self._continue(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.BackToFrontTicket.Kind.COMPLETION: + self._complete(ticket.operation_id, ticket.payload) + else: + self._cancel(ticket.operation_id) diff --git a/src/python/grpcio/grpc/_adapter/rear.py b/src/python/grpcio/grpc/_adapter/rear.py new file mode 100644 index 0000000000..fd6f45f7a7 --- /dev/null +++ b/src/python/grpcio/grpc/_adapter/rear.py @@ -0,0 +1,395 @@ +# 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. + +"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import enum +import logging +import threading +import time + +from grpc._adapter import _common +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 +from grpc.framework.foundation import logging_pool + +_THREAD_POOL_SIZE = 10 + +_INVOCATION_EVENT_KINDS = ( + _low.Event.Kind.METADATA_ACCEPTED, + _low.Event.Kind.FINISH +) + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + """The full state of any tracked RPC. + + Attributes: + call: The _low.Call object for the RPC. + outstanding: The set of Event.Kind values describing expected future events + for the RPC. + active: A boolean indicating whether or not the RPC is active. + common: An _common.RPCState describing additional state for the RPC. + """ + + def __init__(self, call, outstanding, active, common): + self.call = call + self.outstanding = outstanding + self.active = active + self.common = common + + +def _write(operation_id, call, outstanding, write_state, serialized_payload): + if write_state.low is _LowWrite.OPEN: + call.write(serialized_payload, operation_id) + outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + write_state.low = _LowWrite.ACTIVE + elif write_state.low is _LowWrite.ACTIVE: + write_state.pending.append(serialized_payload) + else: + raise ValueError('Write attempted after writes completed!') + + +class RearLink(base_interfaces.RearLink, activated.Activated): + """An invocation-side bridge between RPC Framework and the C-ish _low code.""" + + def __init__( + self, host, port, pool, request_serializers, response_deserializers, + secure, root_certificates, private_key, certificate_chain, + metadata_transformer=None, server_host_override=None): + """Constructor. + + Args: + host: The host to which to connect for RPC service. + port: The port to which to connect for RPC service. + pool: A thread pool. + request_serializers: A dict from RPC method names to request object + serializer behaviors. + response_deserializers: A dict from RPC method names to response object + deserializer behaviors. + secure: A boolean indicating whether or not to use a secure connection. + root_certificates: The PEM-encoded root certificates or None to ask for + them to be retrieved from a default location. + private_key: The PEM-encoded private key to use or None if no private + key should be used. + certificate_chain: The PEM-encoded certificate chain to use or None if + no certificate chain should be used. + metadata_transformer: A function that given a metadata object produces + another metadata to be used in the underlying communication on the + wire. + server_host_override: (For testing only) the target name used for SSL + host name checking. + """ + self._condition = threading.Condition() + self._host = host + self._port = port + self._pool = pool + self._request_serializers = request_serializers + self._response_deserializers = response_deserializers + + self._fore_link = null.NULL_FORE_LINK + self._completion_queue = None + self._channel = None + self._rpc_states = {} + self._spinning = False + if secure: + self._client_credentials = _low.ClientCredentials( + root_certificates, private_key, certificate_chain) + else: + self._client_credentials = None + self._root_certificates = root_certificates + self._private_key = private_key + self._certificate_chain = certificate_chain + self._metadata_transformer = metadata_transformer + self._server_host_override = server_host_override + + def _on_write_event(self, operation_id, event, rpc_state): + if event.write_accepted: + if rpc_state.common.write.pending: + rpc_state.call.write( + rpc_state.common.write.pending.pop(0), operation_id) + rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + elif rpc_state.common.write.high is _common.HighWrite.CLOSED: + rpc_state.call.complete(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + rpc_state.common.write.low = _LowWrite.CLOSED + else: + rpc_state.common.write.low = _LowWrite.OPEN + else: + logging.error('RPC write not accepted! Event: %s', (event,)) + rpc_state.active = False + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _on_read_event(self, operation_id, event, rpc_state): + if event.bytes is not None: + rpc_state.call.read(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) + + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.CONTINUATION, + rpc_state.common.deserializer(event.bytes)) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _on_complete_event(self, operation_id, event, rpc_state): + if not event.complete_accepted: + logging.error('RPC complete not accepted! Event: %s', (event,)) + rpc_state.active = False + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, + base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + # TODO(nathaniel): Metadata support. + def _on_metadata_event(self, operation_id, event, rpc_state): # pylint: disable=unused-argument + rpc_state.call.read(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) + + def _on_finish_event(self, operation_id, event, rpc_state): + """Handle termination of an RPC.""" + # TODO(nathaniel): Cover all statuses. + if event.status.code is _low.Code.OK: + 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.DEADLINE_EXCEEDED: + kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION + else: + kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE + ticket = base_interfaces.BackToFrontTicket( + operation_id, rpc_state.common.sequence_number, kind, None) + rpc_state.common.sequence_number += 1 + self._fore_link.accept_back_to_front_ticket(ticket) + + def _spin(self, completion_queue): + while True: + event = completion_queue.get(None) + operation_id = event.tag + + with self._condition: + rpc_state = self._rpc_states[operation_id] + rpc_state.outstanding.remove(event.kind) + if rpc_state.active and self._completion_queue is not None: + if event.kind is _low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.METADATA_ACCEPTED: + self._on_metadata_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.READ_ACCEPTED: + self._on_read_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: + self._on_complete_event(operation_id, event, rpc_state) + elif event.kind is _low.Event.Kind.FINISH: + self._on_finish_event(operation_id, event, rpc_state) + else: + logging.error('Illegal RPC event! %s', (event,)) + + if not rpc_state.outstanding: + self._rpc_states.pop(operation_id) + if not self._rpc_states: + self._spinning = False + self._condition.notify_all() + return + + def _invoke(self, operation_id, name, high_state, payload, timeout): + """Invoke an RPC. + + Args: + operation_id: Any object to be used as an operation ID for the RPC. + name: The RPC method name. + high_state: A _common.HighWrite value representing the "high write state" + of the RPC. + payload: A payload object for the RPC or None if no payload was given at + invocation-time. + timeout: A duration of time in seconds to allow for the RPC. + """ + request_serializer = self._request_serializers[name] + call = _low.Call(self._channel, self._completion_queue, name, self._host, time.time() + timeout) + if self._metadata_transformer is not None: + metadata = self._metadata_transformer([]) + for metadata_key, metadata_value in metadata: + call.add_metadata(metadata_key, metadata_value) + call.invoke(self._completion_queue, operation_id, operation_id) + outstanding = set(_INVOCATION_EVENT_KINDS) + + if payload is None: + if high_state is _common.HighWrite.CLOSED: + call.complete(operation_id) + low_state = _LowWrite.CLOSED + outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + else: + low_state = _LowWrite.OPEN + else: + serialized_payload = request_serializer(payload) + call.write(serialized_payload, operation_id) + outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) + low_state = _LowWrite.ACTIVE + + write_state = _common.WriteState(low_state, high_state, []) + common_state = _common.CommonRPCState( + write_state, 0, self._response_deserializers[name], request_serializer) + self._rpc_states[operation_id] = _RPCState( + call, outstanding, True, common_state) + + if not self._spinning: + self._pool.submit(self._spin, self._completion_queue) + self._spinning = True + + def _commence(self, operation_id, name, payload, timeout): + self._invoke(operation_id, name, _common.HighWrite.OPEN, payload, timeout) + + def _continue(self, operation_id, payload): + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is None or not rpc_state.active: + return + + _write( + operation_id, rpc_state.call, rpc_state.outstanding, + rpc_state.common.write, rpc_state.common.serializer(payload)) + + def _complete(self, operation_id, payload): + """Close writes associated with an ongoing RPC. + + Args: + operation_id: Any object being use as an operation ID for the RPC. + payload: A payload object for the RPC (and thus the last payload object + for the RPC) or None if no payload was given along with the instruction + to indicate the end of writes for the RPC. + """ + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is None or not rpc_state.active: + return + + write_state = rpc_state.common.write + if payload is None: + if write_state.low is _LowWrite.OPEN: + rpc_state.call.complete(operation_id) + rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) + write_state.low = _LowWrite.CLOSED + else: + _write( + operation_id, rpc_state.call, rpc_state.outstanding, write_state, + rpc_state.common.serializer(payload)) + write_state.high = _common.HighWrite.CLOSED + + def _entire(self, operation_id, name, payload, timeout): + self._invoke(operation_id, name, _common.HighWrite.CLOSED, payload, timeout) + + def _cancel(self, operation_id): + rpc_state = self._rpc_states.get(operation_id, None) + if rpc_state is not None and rpc_state.active: + rpc_state.call.cancel() + rpc_state.active = False + + def join_fore_link(self, fore_link): + """See base_interfaces.RearLink.join_fore_link for specification.""" + with self._condition: + self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link + + def _start(self): + """Starts this RearLink. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._condition: + self._completion_queue = _low.CompletionQueue() + self._channel = _low.Channel( + '%s:%d' % (self._host, self._port), self._client_credentials, + server_host_override=self._server_host_override) + return self + + def _stop(self): + """Stops this RearLink. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._condition: + self._completion_queue.stop() + self._completion_queue = None + + while self._spinning: + self._condition.wait() + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + def accept_front_to_back_ticket(self, ticket): + """See base_interfaces.RearLink.accept_front_to_back_ticket for spec.""" + with self._condition: + if self._completion_queue is None: + return + + if ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT: + self._commence( + ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CONTINUATION: + self._continue(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMPLETION: + self._complete(ticket.operation_id, ticket.payload) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.ENTIRE: + self._entire( + ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) + elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CANCELLATION: + self._cancel(ticket.operation_id) + else: + # NOTE(nathaniel): All other categories are treated as cancellation. + self._cancel(ticket.operation_id) diff --git a/src/python/grpcio/grpc/_cython/.gitignore b/src/python/grpcio/grpc/_cython/.gitignore new file mode 100644 index 0000000000..c315029288 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/.gitignore @@ -0,0 +1,7 @@ +*.h +*.c +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio/grpc/_cython/README.rst b/src/python/grpcio/grpc/_cython/README.rst new file mode 100644 index 0000000000..c0e66734e8 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/README.rst @@ -0,0 +1,52 @@ +GRPC Python Cython layer +======================== + +Package for the GRPC Python Cython layer. + +What is Cython? +--------------- + +Cython is both a superset of the Python language with extensions for dealing +with C types and a tool that transpiles this superset into C code. It provides +convenient means of statically typing expressions and of converting Python +strings to pointers (among other niceties), thus dramatically smoothing the +Python/C interop by allowing fluid use of APIs in both from the same source. +See the wonderful `Cython website`_. + +Why Cython? +----------- + +- **Python 2 and 3 support** + Cython generated C code has precompiler macros to target both Python 2 and + Python 3 C APIs, even while acting as a superset of just the Python 2 + language (e.g. using ``basestring``). +- **Significantly less semantic noise** + A lot of CPython code is just glue, especially human-error-prone + ``Py_INCREF``-ing and ``Py_DECREF``-ing around error handlers and such. + Cython takes care of that automagically. +- **Possible PyPy support** + One of the major developments in Cython over the past few years was the + addition of support for PyPy. We might soon be able to provide such support + ourselves through our use of Cython. +- **Less Python glue code** + There existed several adapter layers in and around the original CPython code + to smooth the surface exposed to Python due to how much trouble it was to + make such a smooth surface via the CPython API alone. Cython makes writing + such a surface incredibly easy, so these adapter layers may be removed. + +Implications for Users +---------------------- + +Nothing additional will be required for users. PyPI packages will contain +Cython generated C code and thus not necessitate a Cython installation. + +Implications for GRPC Developers +-------------------------------- + +A typical edit-compile-debug cycle now requires Cython. We install Cython in +the ``virtualenv`` generated for the Python tests in this repository, so +initial test runs may take an extra 2+ minutes to complete. Subsequent test +runs won't reinstall ``Cython`` (unless required versions change and the +``virtualenv`` doesn't have installed versions that satisfy the change). + +.. _`Cython website`: http://cython.org/ diff --git a/src/python/grpcio/grpc/_cython/__init__.py b/src/python/grpcio/grpc/_cython/__init__.py new file mode 100644 index 0000000000..b89398809f --- /dev/null +++ b/src/python/grpcio/grpc/_cython/__init__.py @@ -0,0 +1,28 @@ +# 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. diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py b/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py new file mode 100644 index 0000000000..b89398809f --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/__init__.py @@ -0,0 +1,28 @@ +# 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. diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd new file mode 100644 index 0000000000..fe9b81e3d3 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pxd @@ -0,0 +1,37 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class Call: + + cdef grpc.grpc_call *c_call + cdef list references + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx new file mode 100644 index 0000000000..4349786b3a --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx @@ -0,0 +1,82 @@ +# 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. + +cimport cpython + +from grpc._cython._cygrpc cimport records + + +cdef class Call: + + def __cinit__(self): + # Create an *empty* call + self.c_call = NULL + self.references = [] + + def start_batch(self, operations, tag): + if not self.is_valid: + raise ValueError("invalid call object cannot be used from Python") + cdef records.Operations cy_operations = records.Operations(operations) + cdef records.OperationTag operation_tag = records.OperationTag(tag) + operation_tag.operation_call = self + operation_tag.batch_operations = cy_operations + cpython.Py_INCREF(operation_tag) + return grpc.grpc_call_start_batch( + self.c_call, cy_operations.c_ops, cy_operations.c_nops, + operation_tag) + + def cancel(self, + grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, + details=None): + if not self.is_valid: + raise ValueError("invalid call object cannot be used from Python") + if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): + raise ValueError("if error_code is specified, so must details " + "(and vice-versa)") + if isinstance(details, bytes): + pass + elif isinstance(details, basestring): + details = details.encode() + else: + raise TypeError("expected details to be str or bytes") + if error_code != grpc.GRPC_STATUS__DO_NOT_USE: + self.references.append(details) + return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details) + else: + return grpc.grpc_call_cancel(self.c_call) + + def __dealloc__(self): + if self.c_call != NULL: + grpc.grpc_call_destroy(self.c_call) + + # The object *should* always be valid from Python. Used for debugging. + @property + def is_valid(self): + return self.c_call != NULL + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd new file mode 100644 index 0000000000..3e341bf222 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pxd @@ -0,0 +1,36 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class Channel: + + cdef grpc.grpc_channel *c_channel + cdef list references diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx new file mode 100644 index 0000000000..b20313818d --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx @@ -0,0 +1,84 @@ +# 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. + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport records + + +cdef class Channel: + + def __cinit__(self, target, records.ChannelArgs arguments=None, + credentials.ClientCredentials client_credentials=None): + cdef grpc.grpc_channel_args *c_arguments = NULL + self.c_channel = NULL + self.references = [] + if arguments is not None: + c_arguments = &arguments.c_args + if isinstance(target, bytes): + pass + elif isinstance(target, basestring): + target = target.encode() + else: + raise TypeError("expected target to be str or bytes") + if client_credentials is None: + self.c_channel = grpc.grpc_channel_create(target, c_arguments) + else: + self.c_channel = grpc.grpc_secure_channel_create( + client_credentials.c_credentials, target, c_arguments) + self.references.append(client_credentials) + self.references.append(target) + self.references.append(arguments) + + def create_call(self, completion_queue.CompletionQueue queue not None, + method, host, records.Timespec deadline not None): + if queue.is_shutting_down: + raise ValueError("queue must not be shutting down or shutdown") + if isinstance(method, bytes): + pass + elif isinstance(method, basestring): + method = method.encode() + else: + raise TypeError("expected method to be str or bytes") + if isinstance(host, bytes): + pass + elif isinstance(host, basestring): + host = host.encode() + else: + raise TypeError("expected host to be str or bytes") + cdef call.Call operation_call = call.Call() + operation_call.references = [self, method, host, queue] + operation_call.c_call = grpc.grpc_channel_create_call( + self.c_channel, queue.c_completion_queue, method, host, deadline.c_time) + return operation_call + + def __dealloc__(self): + if self.c_channel != NULL: + grpc.grpc_channel_destroy(self.c_channel) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd new file mode 100644 index 0000000000..fd562ad75b --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pxd @@ -0,0 +1,39 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class CompletionQueue: + + cdef grpc.grpc_completion_queue *c_completion_queue + cdef object poll_condition + cdef bint is_polling + cdef bint is_shutting_down + cdef bint is_shutdown diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx new file mode 100644 index 0000000000..886d85360a --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx @@ -0,0 +1,117 @@ +# 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. + +cimport cpython + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport records + +import threading +import time + + +cdef class CompletionQueue: + + def __cinit__(self): + self.c_completion_queue = grpc.grpc_completion_queue_create() + self.is_shutting_down = False + self.is_shutdown = False + self.poll_condition = threading.Condition() + self.is_polling = False + + def poll(self, records.Timespec deadline=None): + # We name this 'poll' to avoid problems with CPython's expectations for + # 'special' methods (like next and __next__). + cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future + cdef records.OperationTag tag = None + cdef object user_tag = None + cdef call.Call operation_call = None + cdef records.CallDetails request_call_details = None + cdef records.Metadata request_metadata = None + cdef records.Operations batch_operations = None + if deadline is not None: + c_deadline = deadline.c_time + cdef grpc.grpc_event event + + # Poll within a critical section + with self.poll_condition: + while self.is_polling: + self.poll_condition.wait(float(deadline) - time.time()) + self.is_polling = True + with nogil: + event = grpc.grpc_completion_queue_next( + self.c_completion_queue, c_deadline) + with self.poll_condition: + self.is_polling = False + self.poll_condition.notify() + + if event.type == grpc.GRPC_QUEUE_TIMEOUT: + return records.Event(event.type, False, None, None, None, None, None) + elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: + self.is_shutdown = True + return records.Event(event.type, True, None, None, None, None, None) + else: + if event.tag != NULL: + tag = event.tag + # We receive event tags only after they've been inc-ref'd elsewhere in + # the code. + cpython.Py_DECREF(tag) + if tag.shutting_down_server is not None: + tag.shutting_down_server.notify_shutdown_complete() + user_tag = tag.user_tag + operation_call = tag.operation_call + request_call_details = tag.request_call_details + request_metadata = tag.request_metadata + batch_operations = tag.batch_operations + if tag.is_new_request: + # Stuff in the tag not explicitly handled by us needs to live through + # the life of the call + operation_call.references.extend(tag.references) + return records.Event( + event.type, event.success, user_tag, operation_call, + request_call_details, request_metadata, batch_operations) + + def shutdown(self): + grpc.grpc_completion_queue_shutdown(self.c_completion_queue) + self.is_shutting_down = True + + def clear(self): + if not self.is_shutting_down: + raise ValueError('queue must be shutting down to be cleared') + while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN: + pass + + def __dealloc__(self): + if self.c_completion_queue != NULL: + # Ensure shutdown, pump the queue + if not self.is_shutting_down: + self.shutdown() + while not self.is_shutdown: + self.poll() + grpc.grpc_completion_queue_destroy(self.c_completion_queue) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd new file mode 100644 index 0000000000..6b74a267e0 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd @@ -0,0 +1,45 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc + + +cdef class ClientCredentials: + + cdef grpc.grpc_credentials *c_credentials + cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair + cdef list references + + +cdef class ServerCredentials: + + cdef grpc.grpc_server_credentials *c_credentials + cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs + cdef size_t c_ssl_pem_key_cert_pairs_count + cdef list references diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx new file mode 100644 index 0000000000..2d74702fbd --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx @@ -0,0 +1,207 @@ +# 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. + +from grpc._cython._cygrpc cimport records + + +cdef class ClientCredentials: + + def __cinit__(self): + self.c_credentials = NULL + self.c_ssl_pem_key_cert_pair.private_key = NULL + self.c_ssl_pem_key_cert_pair.certificate_chain = NULL + self.references = [] + + # The object *can* be invalid in Python if we fail to make the credentials + # (and the core thus returns NULL credentials). Used primarily for debugging. + @property + def is_valid(self): + return self.c_credentials != NULL + + def __dealloc__(self): + if self.c_credentials != NULL: + grpc.grpc_credentials_release(self.c_credentials) + + +cdef class ServerCredentials: + + def __cinit__(self): + self.c_credentials = NULL + + def __dealloc__(self): + if self.c_credentials != NULL: + grpc.grpc_server_credentials_release(self.c_credentials) + + +def client_credentials_google_default(): + cdef ClientCredentials credentials = ClientCredentials(); + credentials.c_credentials = grpc.grpc_google_default_credentials_create() + return credentials + +def client_credentials_ssl(pem_root_certificates, + records.SslPemKeyCertPair ssl_pem_key_cert_pair): + if pem_root_certificates is None: + pass + elif isinstance(pem_root_certificates, bytes): + pass + elif isinstance(pem_root_certificates, basestring): + pem_root_certificates = pem_root_certificates.encode() + else: + raise TypeError("expected str or bytes for pem_root_certificates") + cdef ClientCredentials credentials = ClientCredentials() + cdef const char *c_pem_root_certificates = NULL + if pem_root_certificates is not None: + c_pem_root_certificates = pem_root_certificates + credentials.references.append(pem_root_certificates) + if ssl_pem_key_cert_pair is not None: + credentials.c_credentials = grpc.grpc_ssl_credentials_create( + c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair + ) + credentials.references.append(ssl_pem_key_cert_pair) + else: + credentials.c_credentials = grpc.grpc_ssl_credentials_create( + c_pem_root_certificates, NULL + ) + +def client_credentials_composite_credentials( + ClientCredentials credentials_1 not None, + ClientCredentials credentials_2 not None): + if not credentials_1.is_valid or not credentials_2.is_valid: + raise ValueError("passed credentials must both be valid") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_composite_credentials_create( + credentials_1.c_credentials, credentials_2.c_credentials) + credentials.references.append(credentials_1) + credentials.references.append(credentials_2) + return credentials + +def client_credentials_compute_engine(): + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_compute_engine_credentials_create() + return credentials + +def client_credentials_service_account( + json_key, scope, records.Timespec token_lifetime not None): + if isinstance(json_key, bytes): + pass + elif isinstance(json_key, basestring): + json_key = json_key.encode() + else: + raise TypeError("expected json_key to be str or bytes") + if isinstance(scope, bytes): + pass + elif isinstance(scope, basestring): + scope = scope.encode() + else: + raise TypeError("expected scope to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_service_account_credentials_create( + json_key, scope, token_lifetime.c_time) + credentials.references.extend([json_key, scope]) + return credentials + +#TODO rename to something like client_credentials_service_account_jwt_access. +def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): + if isinstance(json_key, bytes): + pass + elif isinstance(json_key, basestring): + json_key = json_key.encode() + else: + raise TypeError("expected json_key to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_service_account_jwt_access_credentials_create( + json_key, token_lifetime.c_time) + credentials.references.append(json_key) + return credentials + +def client_credentials_refresh_token(json_refresh_token): + if isinstance(json_refresh_token, bytes): + pass + elif isinstance(json_refresh_token, basestring): + json_refresh_token = json_refresh_token.encode() + else: + raise TypeError("expected json_refresh_token to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_refresh_token_credentials_create( + json_refresh_token) + credentials.references.append(json_refresh_token) + return credentials + +def client_credentials_iam(authorization_token, authority_selector): + if isinstance(authorization_token, bytes): + pass + elif isinstance(authorization_token, basestring): + authorization_token = authorization_token.encode() + else: + raise TypeError("expected authorization_token to be str or bytes") + if isinstance(authority_selector, bytes): + pass + elif isinstance(authority_selector, basestring): + authority_selector = authority_selector.encode() + else: + raise TypeError("expected authority_selector to be str or bytes") + cdef ClientCredentials credentials = ClientCredentials() + credentials.c_credentials = grpc.grpc_iam_credentials_create( + authorization_token, authority_selector) + credentials.references.append(authorization_token) + credentials.references.append(authority_selector) + return credentials + +def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): + if pem_root_certs is None: + pass + elif isinstance(pem_root_certs, bytes): + pass + elif isinstance(pem_root_certs, basestring): + pem_root_certs = pem_root_certs.encode() + else: + raise TypeError("expected pem_root_certs to be str or bytes") + pem_key_cert_pairs = list(pem_key_cert_pairs) + for pair in pem_key_cert_pairs: + if not isinstance(pair, records.SslPemKeyCertPair): + raise TypeError("expected pem_key_cert_pairs to be sequence of " + "records.SslPemKeyCertPair") + cdef ServerCredentials credentials = ServerCredentials() + credentials.references.append(pem_key_cert_pairs) + credentials.references.append(pem_root_certs) + credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) + credentials.c_ssl_pem_key_cert_pairs = ( + grpc.gpr_malloc( + sizeof(grpc.grpc_ssl_pem_key_cert_pair) * + credentials.c_ssl_pem_key_cert_pairs_count + )) + for i in range(credentials.c_ssl_pem_key_cert_pairs_count): + credentials.c_ssl_pem_key_cert_pairs[i] = ( + (pem_key_cert_pairs[i]).c_pair) + credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( + pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, + credentials.c_ssl_pem_key_cert_pairs_count + ) + return credentials + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd new file mode 100644 index 0000000000..d065383587 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd @@ -0,0 +1,342 @@ +# 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. + +cimport libc.time + + +cdef extern from "grpc/support/alloc.h": + void *gpr_malloc(size_t size) + void gpr_free(void *ptr) + void *gpr_realloc(void *p, size_t size) + +cdef extern from "grpc/support/slice.h": + ctypedef struct gpr_slice: + # don't worry about writing out the members of gpr_slice; we never access + # them directly. + pass + + gpr_slice gpr_slice_ref(gpr_slice s) + void gpr_slice_unref(gpr_slice s) + gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) + gpr_slice gpr_slice_new_with_len( + void *p, size_t len, void (*destroy)(void *, size_t)) + gpr_slice gpr_slice_malloc(size_t length) + gpr_slice gpr_slice_from_copied_string(const char *source) + gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len) + + # Declare functions for function-like macros (because Cython)... + void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s) + size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s) + + +cdef extern from "grpc/support/port_platform.h": + # As long as the header file gets this type right, we don't need to get this + # type exactly; just close enough that the operations will be supported in the + # underlying C layers. + ctypedef unsigned int gpr_uint32 + + +cdef extern from "grpc/support/time.h": + + ctypedef struct gpr_timespec: + libc.time.time_t seconds "tv_sec" + int nanoseconds "tv_nsec" + + cdef gpr_timespec gpr_time_0 + cdef gpr_timespec gpr_inf_future + cdef gpr_timespec gpr_inf_past + + gpr_timespec gpr_now() + + +cdef extern from "grpc/status.h": + ctypedef enum grpc_status_code: + GRPC_STATUS_OK + GRPC_STATUS_CANCELLED + GRPC_STATUS_UNKNOWN + GRPC_STATUS_INVALID_ARGUMENT + GRPC_STATUS_DEADLINE_EXCEEDED + GRPC_STATUS_NOT_FOUND + GRPC_STATUS_ALREADY_EXISTS + GRPC_STATUS_PERMISSION_DENIED + GRPC_STATUS_UNAUTHENTICATED + GRPC_STATUS_RESOURCE_EXHAUSTED + GRPC_STATUS_FAILED_PRECONDITION + GRPC_STATUS_ABORTED + GRPC_STATUS_OUT_OF_RANGE + GRPC_STATUS_UNIMPLEMENTED + GRPC_STATUS_INTERNAL + GRPC_STATUS_UNAVAILABLE + GRPC_STATUS_DATA_LOSS + GRPC_STATUS__DO_NOT_USE + + +cdef extern from "grpc/byte_buffer_reader.h": + struct grpc_byte_buffer_reader: + # We don't care about the internals + pass + + +cdef extern from "grpc/byte_buffer.h": + ctypedef struct grpc_byte_buffer: + # We don't care about the internals. + pass + + grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, + size_t nslices) + size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) + void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer) + + void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, + grpc_byte_buffer *buffer) + int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, + gpr_slice *slice) + void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) + + +cdef extern from "grpc/grpc.h": + + ctypedef struct grpc_completion_queue: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_channel: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_server: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_call: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef enum grpc_arg_type: + grpc_arg_string "GRPC_ARG_STRING" + grpc_arg_integer "GRPC_ARG_INTEGER" + grpc_arg_pointer "GRPC_ARG_POINTER" + + ctypedef struct grpc_arg_value_pointer: + void *address "p" + void *(*copy)(void *) + void (*destroy)(void *) + + union grpc_arg_value: + char *string + int integer + grpc_arg_value_pointer pointer + + ctypedef struct grpc_arg: + grpc_arg_type type + char *key + grpc_arg_value value + + ctypedef struct grpc_channel_args: + size_t arguments_length "num_args" + grpc_arg *arguments "args" + + ctypedef enum grpc_call_error: + GRPC_CALL_OK + GRPC_CALL_ERROR + GRPC_CALL_ERROR_NOT_ON_SERVER + GRPC_CALL_ERROR_NOT_ON_CLIENT + GRPC_CALL_ERROR_ALREADY_ACCEPTED + GRPC_CALL_ERROR_ALREADY_INVOKED + GRPC_CALL_ERROR_NOT_INVOKED + GRPC_CALL_ERROR_ALREADY_FINISHED + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS + GRPC_CALL_ERROR_INVALID_FLAGS + GRPC_CALL_ERROR_INVALID_METADATA + + ctypedef struct grpc_metadata: + const char *key + const char *value + size_t value_length + # ignore the 'internal_data.obfuscated' fields. + + ctypedef enum grpc_completion_type: + GRPC_QUEUE_SHUTDOWN + GRPC_QUEUE_TIMEOUT + GRPC_OP_COMPLETE + + ctypedef struct grpc_event: + grpc_completion_type type + int success + void *tag + + ctypedef struct grpc_metadata_array: + size_t count + size_t capacity + grpc_metadata *metadata + + void grpc_metadata_array_init(grpc_metadata_array *array) + void grpc_metadata_array_destroy(grpc_metadata_array *array) + + ctypedef struct grpc_call_details: + char *method + size_t method_capacity + char *host + size_t host_capacity + gpr_timespec deadline + + void grpc_call_details_init(grpc_call_details *details) + void grpc_call_details_destroy(grpc_call_details *details) + + ctypedef enum grpc_op_type: + GRPC_OP_SEND_INITIAL_METADATA + GRPC_OP_SEND_MESSAGE + GRPC_OP_SEND_CLOSE_FROM_CLIENT + GRPC_OP_SEND_STATUS_FROM_SERVER + GRPC_OP_RECV_INITIAL_METADATA + GRPC_OP_RECV_MESSAGE + GRPC_OP_RECV_STATUS_ON_CLIENT + GRPC_OP_RECV_CLOSE_ON_SERVER + + ctypedef struct grpc_op_data_send_initial_metadata: + size_t count + grpc_metadata *metadata + + ctypedef struct grpc_op_data_send_status_from_server: + size_t trailing_metadata_count + grpc_metadata *trailing_metadata + grpc_status_code status + const char *status_details + + ctypedef struct grpc_op_data_recv_status_on_client: + grpc_metadata_array *trailing_metadata + grpc_status_code *status + char **status_details + size_t *status_details_capacity + + ctypedef struct grpc_op_data_recv_close_on_server: + int *cancelled + + union grpc_op_data: + grpc_op_data_send_initial_metadata send_initial_metadata + grpc_byte_buffer *send_message + grpc_op_data_send_status_from_server send_status_from_server + grpc_metadata_array *receive_initial_metadata "recv_initial_metadata" + grpc_byte_buffer **receive_message "recv_message" + grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client" + grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server" + + ctypedef struct grpc_op: + grpc_op_type type "op" + gpr_uint32 flags + grpc_op_data data + + void grpc_init() + void grpc_shutdown() + + grpc_completion_queue *grpc_completion_queue_create() + grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, + gpr_timespec deadline) nogil + void grpc_completion_queue_shutdown(grpc_completion_queue *cq) + void grpc_completion_queue_destroy(grpc_completion_queue *cq) + + grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag) + grpc_call_error grpc_call_cancel(grpc_call *call) + grpc_call_error grpc_call_cancel_with_status(grpc_call *call, + grpc_status_code status, + const char *description) + void grpc_call_destroy(grpc_call *call) + + + grpc_channel *grpc_channel_create(const char *target, + const grpc_channel_args *args) + grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_completion_queue *completion_queue, + const char *method, const char *host, + gpr_timespec deadline) + void grpc_channel_destroy(grpc_channel *channel) + + grpc_server *grpc_server_create(const grpc_channel_args *args) + grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call **call, grpc_call_details *details, + grpc_metadata_array *request_metadata, grpc_completion_queue + *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void + *tag_new) + void grpc_server_register_completion_queue(grpc_server *server, + grpc_completion_queue *cq) + int grpc_server_add_http2_port(grpc_server *server, const char *addr) + void grpc_server_start(grpc_server *server) + void grpc_server_shutdown_and_notify( + grpc_server *server, grpc_completion_queue *cq, void *tag) + void grpc_server_cancel_all_calls(grpc_server *server) + void grpc_server_destroy(grpc_server *server) + + +cdef extern from "grpc/grpc_security.h": + + ctypedef struct grpc_ssl_pem_key_cert_pair: + const char *private_key + const char *certificate_chain "cert_chain" + + ctypedef struct grpc_credentials: + # We don't care about the internals (and in fact don't know them) + pass + + grpc_credentials *grpc_google_default_credentials_create() + grpc_credentials *grpc_ssl_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) + + grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, + grpc_credentials *creds2) + grpc_credentials *grpc_compute_engine_credentials_create() + grpc_credentials *grpc_service_account_credentials_create( + const char *json_key, const char *scope, gpr_timespec token_lifetime) + grpc_credentials *grpc_service_account_jwt_access_credentials_create(const char *json_key, + gpr_timespec token_lifetime) + grpc_credentials *grpc_refresh_token_credentials_create( + const char *json_refresh_token) + grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, + const char *authority_selector) + void grpc_credentials_release(grpc_credentials *creds) + + grpc_channel *grpc_secure_channel_create( + grpc_credentials *creds, const char *target, + const grpc_channel_args *args) + + ctypedef struct grpc_server_credentials: + # We don't care about the internals (and in fact don't know them) + pass + + grpc_server_credentials *grpc_ssl_server_credentials_create( + const char *pem_root_certs, + grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs); + void grpc_server_credentials_release(grpc_server_credentials *creds) + + int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) + + grpc_call_error grpc_call_set_credentials(grpc_call *call, + grpc_credentials *creds) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd new file mode 100644 index 0000000000..9ee487882a --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd @@ -0,0 +1,129 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport server + + +cdef class Timespec: + + cdef grpc.gpr_timespec c_time + + +cdef class CallDetails: + + cdef grpc.grpc_call_details c_details + + +cdef class OperationTag: + + cdef object user_tag + cdef list references + # This allows CompletionQueue to notify the Python Server object that the + # underlying GRPC core server has shutdown + cdef server.Server shutting_down_server + cdef call.Call operation_call + cdef CallDetails request_call_details + cdef Metadata request_metadata + cdef Operations batch_operations + cdef bint is_new_request + + +cdef class Event: + + cdef readonly grpc.grpc_completion_type type + cdef readonly bint success + cdef readonly object tag + + # For operations with calls + cdef readonly call.Call operation_call + + # For Server.request_call + cdef readonly CallDetails request_call_details + cdef readonly Metadata request_metadata + + # For Call.start_batch + cdef readonly Operations batch_operations + + +cdef class ByteBuffer: + + cdef grpc.grpc_byte_buffer *c_byte_buffer + + +cdef class SslPemKeyCertPair: + + cdef grpc.grpc_ssl_pem_key_cert_pair c_pair + cdef readonly object private_key, certificate_chain + + +cdef class ChannelArg: + + cdef grpc.grpc_arg c_arg + cdef readonly object key, value + + +cdef class ChannelArgs: + + cdef grpc.grpc_channel_args c_args + cdef list args + + +cdef class Metadatum: + + cdef grpc.grpc_metadata c_metadata + cdef object _key, _value + + +cdef class Metadata: + + cdef grpc.grpc_metadata_array c_metadata_array + cdef object metadata + + +cdef class Operation: + + cdef grpc.grpc_op c_op + cdef ByteBuffer _received_message + cdef Metadata _received_metadata + cdef grpc.grpc_status_code _received_status_code + cdef char *_received_status_details + cdef size_t _received_status_details_capacity + cdef int _received_cancelled + cdef readonly bint is_valid + cdef object references + + +cdef class Operations: + + cdef grpc.grpc_op *c_ops + cdef size_t c_nops + cdef list operations + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx new file mode 100644 index 0000000000..4814769fd2 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx @@ -0,0 +1,575 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport server + + +class StatusCode: + ok = grpc.GRPC_STATUS_OK + cancelled = grpc.GRPC_STATUS_CANCELLED + unknown = grpc.GRPC_STATUS_UNKNOWN + invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT + deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED + not_found = grpc.GRPC_STATUS_NOT_FOUND + already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS + permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED + unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED + resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED + failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION + aborted = grpc.GRPC_STATUS_ABORTED + out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE + unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED + internal = grpc.GRPC_STATUS_INTERNAL + unavailable = grpc.GRPC_STATUS_UNAVAILABLE + data_loss = grpc.GRPC_STATUS_DATA_LOSS + + +class CallError: + ok = grpc.GRPC_CALL_OK + error = grpc.GRPC_CALL_ERROR + not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER + not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT + already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED + already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED + not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED + already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED + too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS + invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS + invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA + + +class CompletionType: + queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN + queue_timeout = grpc.GRPC_QUEUE_TIMEOUT + operation_complete = grpc.GRPC_OP_COMPLETE + + +class OperationType: + send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA + send_message = grpc.GRPC_OP_SEND_MESSAGE + send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT + send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER + receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA + receive_message = grpc.GRPC_OP_RECV_MESSAGE + receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT + receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER + + +cdef class Timespec: + + def __cinit__(self, time): + if time is None: + self.c_time = grpc.gpr_now() + elif isinstance(time, float): + if time == float("+inf"): + self.c_time = grpc.gpr_inf_future + elif time == float("-inf"): + self.c_time = grpc.gpr_inf_past + else: + self.c_time.seconds = time + self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 + else: + raise TypeError("expected time to be float") + + @property + def seconds(self): + return self.c_time.seconds + + @property + def nanoseconds(self): + return self.c_time.nanoseconds + + def __float__(self): + return self.c_time.seconds + self.c_time.nanoseconds / 1e9 + + infinite_future = Timespec(float("+inf")) + infinite_past = Timespec(float("-inf")) + + +cdef class CallDetails: + + def __cinit__(self): + grpc.grpc_call_details_init(&self.c_details) + + def __dealloc__(self): + grpc.grpc_call_details_destroy(&self.c_details) + + @property + def method(self): + if self.c_details.method != NULL: + return self.c_details.method + else: + return None + + @property + def host(self): + if self.c_details.host != NULL: + return self.c_details.host + else: + return None + + @property + def deadline(self): + timespec = Timespec(float("-inf")) + timespec.c_time = self.c_details.deadline + return timespec + + +cdef class OperationTag: + + def __cinit__(self, user_tag): + self.user_tag = user_tag + self.references = [] + + +cdef class Event: + + def __cinit__(self, grpc.grpc_completion_type type, bint success, + object tag, call.Call operation_call, + CallDetails request_call_details, + Metadata request_metadata, + Operations batch_operations): + self.type = type + self.success = success + self.tag = tag + self.operation_call = operation_call + self.request_call_details = request_call_details + self.request_metadata = request_metadata + self.batch_operations = batch_operations + + +cdef class ByteBuffer: + + def __cinit__(self, data): + if data is None: + self.c_byte_buffer = NULL + return + if isinstance(data, bytes): + pass + elif isinstance(data, basestring): + data = data.encode() + else: + raise TypeError("expected value to be of type str or bytes") + + cdef char *c_data = data + data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) + self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create( + &data_slice, 1) + grpc.gpr_slice_unref(data_slice) + + def bytes(self): + cdef grpc.grpc_byte_buffer_reader reader + cdef grpc.gpr_slice data_slice + cdef size_t data_slice_length + cdef void *data_slice_pointer + if self.c_byte_buffer != NULL: + grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer) + result = b"" + while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice): + data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice) + data_slice_length = grpc.gpr_slice_length(data_slice) + result += (data_slice_pointer)[:data_slice_length] + grpc.grpc_byte_buffer_reader_destroy(&reader) + return result + else: + return None + + def __len__(self): + if self.c_byte_buffer != NULL: + return grpc.grpc_byte_buffer_length(self.c_byte_buffer) + else: + return 0 + + def __str__(self): + return self.bytes() + + def __dealloc__(self): + if self.c_byte_buffer != NULL: + grpc.grpc_byte_buffer_destroy(self.c_byte_buffer) + + +cdef class SslPemKeyCertPair: + + def __cinit__(self, private_key, certificate_chain): + if isinstance(private_key, bytes): + self.private_key = private_key + elif isinstance(private_key, basestring): + self.private_key = private_key.encode() + else: + raise TypeError("expected private_key to be of type str or bytes") + if isinstance(certificate_chain, bytes): + self.certificate_chain = certificate_chain + elif isinstance(certificate_chain, basestring): + self.certificate_chain = certificate_chain.encode() + else: + raise TypeError("expected certificate_chain to be of type str or bytes " + "or int") + self.c_pair.private_key = self.private_key + self.c_pair.certificate_chain = self.certificate_chain + + +cdef class ChannelArg: + + def __cinit__(self, key, value): + if isinstance(key, bytes): + self.key = key + elif isinstance(key, basestring): + self.key = key.encode() + else: + raise TypeError("expected key to be of type str or bytes") + if isinstance(value, bytes): + self.value = value + self.c_arg.type = grpc.GRPC_ARG_STRING + self.c_arg.value.string = self.value + elif isinstance(value, basestring): + self.value = value.encode() + self.c_arg.type = grpc.GRPC_ARG_STRING + self.c_arg.value.string = self.value + elif isinstance(value, int): + self.value = int(value) + self.c_arg.type = grpc.GRPC_ARG_INTEGER + self.c_arg.value.integer = self.value + else: + raise TypeError("expected value to be of type str or bytes or int") + self.c_arg.key = self.key + + +cdef class ChannelArgs: + + def __cinit__(self, args): + self.args = list(args) + for arg in self.args: + if not isinstance(arg, ChannelArg): + raise TypeError("expected list of ChannelArg") + self.c_args.arguments_length = len(self.args) + self.c_args.arguments = grpc.gpr_malloc( + self.c_args.arguments_length*sizeof(grpc.grpc_arg) + ) + for i in range(self.c_args.arguments_length): + self.c_args.arguments[i] = (self.args[i]).c_arg + + def __dealloc__(self): + grpc.gpr_free(self.c_args.arguments) + + def __len__(self): + # self.args is never stale; it's only updated from this file + return len(self.args) + + def __getitem__(self, size_t i): + # self.args is never stale; it's only updated from this file + return self.args[i] + + +cdef class Metadatum: + + def __cinit__(self, key, value): + if isinstance(key, bytes): + self._key = key + elif isinstance(key, basestring): + self._key = key.encode() + else: + raise TypeError("expected key to be of type str or bytes") + if isinstance(value, bytes): + self._value = value + elif isinstance(value, basestring): + self._value = value.encode() + else: + raise TypeError("expected value to be of type str or bytes") + self.c_metadata.key = self._key + self.c_metadata.value = self._value + self.c_metadata.value_length = len(self._value) + + @property + def key(self): + return self.c_metadata.key + + @property + def value(self): + return self.c_metadata.value[:self.c_metadata.value_length] + + def __len__(self): + return 2 + + def __getitem__(self, size_t i): + if i == 0: + return self.key + elif i == 1: + return self.value + else: + raise IndexError("index must be 0 (key) or 1 (value)") + + def __iter__(self): + return iter((self.key, self.value)) + + +cdef class _MetadataIterator: + + cdef size_t i + cdef Metadata metadata + + def __cinit__(self, Metadata metadata not None): + self.i = 0 + self.metadata = metadata + + def __next__(self): + if self.i < len(self.metadata): + result = self.metadata[self.i] + self.i = self.i + 1 + return result + else: + raise StopIteration() + + +cdef class Metadata: + + def __cinit__(self, metadata): + self.metadata = list(metadata) + for metadatum in metadata: + if not isinstance(metadatum, Metadatum): + raise TypeError("expected list of Metadatum") + grpc.grpc_metadata_array_init(&self.c_metadata_array) + self.c_metadata_array.count = len(self.metadata) + self.c_metadata_array.capacity = len(self.metadata) + self.c_metadata_array.metadata = grpc.gpr_malloc( + self.c_metadata_array.count*sizeof(grpc.grpc_metadata) + ) + for i in range(self.c_metadata_array.count): + self.c_metadata_array.metadata[i] = ( + (self.metadata[i]).c_metadata) + + def __dealloc__(self): + # this frees the allocated memory for the grpc_metadata_array (although + # it'd be nice if that were documented somewhere...) TODO(atash): document + # this in the C core + grpc.grpc_metadata_array_destroy(&self.c_metadata_array) + + def __len__(self): + return self.c_metadata_array.count + + def __getitem__(self, size_t i): + return Metadatum( + key=self.c_metadata_array.metadata[i].key, + value=self.c_metadata_array.metadata[i].value[ + :self.c_metadata_array.metadata[i].value_length]) + + def __iter__(self): + return _MetadataIterator(self) + + +cdef class Operation: + + def __cinit__(self): + self.references = [] + self._received_status_details = NULL + self._received_status_details_capacity = 0 + self.is_valid = False + + @property + def type(self): + return self.c_op.type + + @property + def received_message(self): + if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: + raise TypeError("self must be an operation receiving a message") + return self._received_message + + @property + def received_metadata(self): + if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and + self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): + raise TypeError("self must be an operation receiving metadata") + return self._received_metadata + + @property + def received_status_code(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + raise TypeError("self must be an operation receiving a status code") + return self._received_status_code + + @property + def received_status_details(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + raise TypeError("self must be an operation receiving status details") + if self._received_status_details: + return self._received_status_details + else: + return None + + @property + def received_cancelled(self): + if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: + raise TypeError("self must be an operation receiving cancellation " + "information") + return False if self._received_cancelled == 0 else True + + def __dealloc__(self): + # We *almost* don't need to do anything; most of the objects are handled by + # Python. The remaining one(s) are primitive fields filled in by GRPC core. + # This means that we need to clean up after receive_status_on_client. + if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + grpc.gpr_free(self._received_status_details) + +def operation_send_initial_metadata(Metadata metadata): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA + op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count + op.c_op.data.send_initial_metadata.metadata = ( + metadata.c_metadata_array.metadata) + op.references.append(metadata) + op.is_valid = True + return op + +def operation_send_message(data): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE + byte_buffer = ByteBuffer(data) + op.c_op.data.send_message = byte_buffer.c_byte_buffer + op.references.append(byte_buffer) + op.is_valid = True + return op + +def operation_send_close_from_client(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT + op.is_valid = True + return op + +def operation_send_status_from_server( + Metadata metadata, grpc.grpc_status_code code, details): + if isinstance(details, bytes): + pass + elif isinstance(details, basestring): + details = details.encode() + else: + raise TypeError("expected a str or bytes object for details") + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER + op.c_op.data.send_status_from_server.trailing_metadata_count = ( + metadata.c_metadata_array.count) + op.c_op.data.send_status_from_server.trailing_metadata = ( + metadata.c_metadata_array.metadata) + op.c_op.data.send_status_from_server.status = code + op.c_op.data.send_status_from_server.status_details = details + op.references.append(metadata) + op.references.append(details) + op.is_valid = True + return op + +def operation_receive_initial_metadata(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA + op._received_metadata = Metadata([]) + op.c_op.data.receive_initial_metadata = ( + &op._received_metadata.c_metadata_array) + op.is_valid = True + return op + +def operation_receive_message(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE + op._received_message = ByteBuffer(None) + # n.b. the c_op.data.receive_message field needs to be deleted by us, + # anyway, so we just let that be handled by the ByteBuffer() we allocated + # the line before. + op.c_op.data.receive_message = &op._received_message.c_byte_buffer + op.is_valid = True + return op + +def operation_receive_status_on_client(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT + op._received_metadata = Metadata([]) + op.c_op.data.receive_status_on_client.trailing_metadata = ( + &op._received_metadata.c_metadata_array) + op.c_op.data.receive_status_on_client.status = ( + &op._received_status_code) + op.c_op.data.receive_status_on_client.status_details = ( + &op._received_status_details) + op.c_op.data.receive_status_on_client.status_details_capacity = ( + &op._received_status_details_capacity) + op.is_valid = True + return op + +def operation_receive_close_on_server(): + cdef Operation op = Operation() + op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER + op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled + op.is_valid = True + return op + + +cdef class _OperationsIterator: + + cdef size_t i + cdef Operations operations + + def __cinit__(self, Operations operations not None): + self.i = 0 + self.operations = operations + + def __next__(self): + if self.i < len(self.operations): + result = self.operations[self.i] + self.i = self.i + 1 + return result + else: + raise StopIteration() + + +cdef class Operations: + + def __cinit__(self, operations): + self.operations = list(operations) # normalize iterable + self.c_ops = NULL + self.c_nops = 0 + for operation in self.operations: + if not isinstance(operation, Operation): + raise TypeError("expected operations to be iterable of Operation") + self.c_nops = len(self.operations) + self.c_ops = grpc.gpr_malloc( + sizeof(grpc.grpc_op)*self.c_nops) + for i in range(self.c_nops): + self.c_ops[i] = ((self.operations[i])).c_op + + def __len__(self): + return self.c_nops + + def __getitem__(self, size_t i): + # self.operations is never stale; it's only updated from this file + return self.operations[i] + + def __dealloc__(self): + grpc.gpr_free(self.c_ops) + + def __iter__(self): + return _OperationsIterator(self) + diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd new file mode 100644 index 0000000000..0257542a03 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pxd @@ -0,0 +1,45 @@ +# 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. + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport completion_queue + + +cdef class Server: + + cdef grpc.grpc_server *c_server + cdef bint is_started # start has been called + cdef bint is_shutting_down # shutdown has been called + cdef bint is_shutdown # notification of complete shutdown received + # used at dealloc when user forgets to shutdown + cdef completion_queue.CompletionQueue backup_shutdown_queue + cdef list references + cdef list registered_completion_queues + + cdef notify_shutdown_complete(self) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx new file mode 100644 index 0000000000..dcf9d38337 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx @@ -0,0 +1,158 @@ +# 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. + +cimport cpython + +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport records + +import time + + +cdef class Server: + + def __cinit__(self, records.ChannelArgs arguments=None): + cdef grpc.grpc_channel_args *c_arguments = NULL + self.references = [] + self.registered_completion_queues = [] + if arguments is not None: + c_arguments = &arguments.c_args + self.references.append(arguments) + self.c_server = grpc.grpc_server_create(c_arguments) + self.is_started = False + self.is_shutting_down = False + self.is_shutdown = False + + def request_call( + self, completion_queue.CompletionQueue call_queue not None, + completion_queue.CompletionQueue server_queue not None, tag): + if not self.is_started or self.is_shutting_down: + raise ValueError("server must be started and not shutting down") + if server_queue not in self.registered_completion_queues: + raise ValueError("server_queue must be a registered completion queue") + cdef records.OperationTag operation_tag = records.OperationTag(tag) + operation_tag.operation_call = call.Call() + operation_tag.request_call_details = records.CallDetails() + operation_tag.request_metadata = records.Metadata([]) + operation_tag.references.extend([self, call_queue, server_queue]) + operation_tag.is_new_request = True + operation_tag.batch_operations = records.Operations([]) + cpython.Py_INCREF(operation_tag) + return grpc.grpc_server_request_call( + self.c_server, &operation_tag.operation_call.c_call, + &operation_tag.request_call_details.c_details, + &operation_tag.request_metadata.c_metadata_array, + call_queue.c_completion_queue, server_queue.c_completion_queue, + operation_tag) + + def register_completion_queue( + self, completion_queue.CompletionQueue queue not None): + if self.is_started: + raise ValueError("cannot register completion queues after start") + grpc.grpc_server_register_completion_queue( + self.c_server, queue.c_completion_queue) + self.registered_completion_queues.append(queue) + + def start(self): + if self.is_started: + raise ValueError("the server has already started") + self.backup_shutdown_queue = completion_queue.CompletionQueue() + self.register_completion_queue(self.backup_shutdown_queue) + self.is_started = True + grpc.grpc_server_start(self.c_server) + + def add_http2_port(self, address, + credentials.ServerCredentials server_credentials=None): + if isinstance(address, bytes): + pass + elif isinstance(address, basestring): + address = address.encode() + else: + raise TypeError("expected address to be a str or bytes") + self.references.append(address) + if server_credentials is not None: + self.references.append(server_credentials) + return grpc.grpc_server_add_secure_http2_port( + self.c_server, address, server_credentials.c_credentials) + else: + return grpc.grpc_server_add_http2_port(self.c_server, address) + + def shutdown(self, completion_queue.CompletionQueue queue not None, tag): + cdef records.OperationTag operation_tag + if queue.is_shutting_down: + raise ValueError("queue must be live") + elif not self.is_started: + raise ValueError("the server hasn't started yet") + elif self.is_shutting_down: + return + elif queue not in self.registered_completion_queues: + raise ValueError("expected registered completion queue") + else: + self.is_shutting_down = True + operation_tag = records.OperationTag(tag) + operation_tag.shutting_down_server = self + operation_tag.references.extend([self, queue]) + cpython.Py_INCREF(operation_tag) + grpc.grpc_server_shutdown_and_notify( + self.c_server, queue.c_completion_queue, + operation_tag) + + cdef notify_shutdown_complete(self): + # called only by a completion queue on receiving our shutdown operation tag + self.is_shutdown = True + + def cancel_all_calls(self): + if not self.is_shutting_down: + raise ValueError("the server must be shutting down to cancel all calls") + elif self.is_shutdown: + return + else: + grpc.grpc_server_cancel_all_calls(self.c_server) + + def __dealloc__(self): + if self.c_server != NULL: + if not self.is_started: + pass + elif self.is_shutdown: + pass + elif not self.is_shutting_down: + # the user didn't call shutdown - use our backup queue + self.shutdown(self.backup_shutdown_queue, None) + # and now we wait + while not self.is_shutdown: + self.backup_shutdown_queue.poll() + else: + # We're in the process of shutting down, but have not shutdown; can't do + # much but repeatedly release the GIL and wait + while not self.is_shutdown: + time.sleep(0) + grpc.grpc_server_destroy(self.c_server) + diff --git a/src/python/grpcio/grpc/_cython/adapter_low.py b/src/python/grpcio/grpc/_cython/adapter_low.py new file mode 100644 index 0000000000..2bb468eece --- /dev/null +++ b/src/python/grpcio/grpc/_cython/adapter_low.py @@ -0,0 +1,106 @@ +# 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. + + +# Adapter from grpc._cython.types to the surface expected by +# grpc._adapter._intermediary_low. +# +# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove +# both grpc._adapter._intermediary_low and this file. The fore and rear links in +# grpc._adapter should be able to use grpc._cython.types directly. + +from grpc._adapter import _types as type_interfaces +from grpc._cython import cygrpc + + +class ClientCredentials(object): + def __init__(self): + raise NotImplementedError() + + @staticmethod + def google_default(): + raise NotImplementedError() + + @staticmethod + def ssl(): + raise NotImplementedError() + + @staticmethod + def composite(): + raise NotImplementedError() + + @staticmethod + def compute_engine(): + raise NotImplementedError() + + @staticmethod + def service_account(): + raise NotImplementedError() + + @staticmethod + def jwt(): + raise NotImplementedError() + + @staticmethod + def refresh_token(): + raise NotImplementedError() + + @staticmethod + def iam(): + raise NotImplementedError() + + +class ServerCredentials(object): + def __init__(self): + raise NotImplementedError() + + @staticmethod + def ssl(): + raise NotImplementedError() + + +class CompletionQueue(type_interfaces.CompletionQueue): + def __init__(self): + raise NotImplementedError() + + +class Call(type_interfaces.Call): + def __init__(self): + raise NotImplementedError() + + +class Channel(type_interfaces.Channel): + def __init__(self): + raise NotImplementedError() + + +class Server(type_interfaces.Server): + def __init__(self): + raise NotImplementedError() + diff --git a/src/python/grpcio/grpc/_cython/adapter_low_test.py b/src/python/grpcio/grpc/_cython/adapter_low_test.py new file mode 100644 index 0000000000..9bab930e56 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/adapter_low_test.py @@ -0,0 +1,187 @@ +# 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. + +# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in +# grpc._cython.low should transparently support the semantics expected of +# grpc._adapter._low. + +import time +import unittest + +from grpc._adapter import _types +from grpc._cython import adapter_low as _low + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + 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 + del self.server + + @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') + 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.assertEqual(_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.assertEqual(_types.CallError.OK, client_start_batch_result) + + request_event = self.server_completion_queue.next(DEADLINE) + self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) + self.assertIsInstance(request_event.call, _low.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(1, len(request_event.results)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.results[0].initial_metadata)) + self.assertEqual(METHOD, request_event.call_details.method) + self.assertEqual(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.assertEqual(_types.CallError.OK, server_start_batch_result) + + client_event = self.client_completion_queue.next(DEADLINE) + server_event = self.server_completion_queue.next(DEADLINE) + + self.assertEqual(6, len(client_event.results)) + found_client_op_types = set() + for client_result in client_event.results: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == _types.OpType.RECV_INITIAL_METADATA: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.initial_metadata)) + elif client_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(RESPONSE, client_result.message) + elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.trailing_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) + self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) + self.assertEqual(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.assertEqual(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.assertEqual(REQUEST, server_result.message) + elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: + self.assertFalse(server_result.cancelled) + self.assertEqual(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__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx new file mode 100644 index 0000000000..f4d9661580 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -0,0 +1,107 @@ +# 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. + +cimport cpython + +from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport call +from grpc._cython._cygrpc cimport channel +from grpc._cython._cygrpc cimport credentials +from grpc._cython._cygrpc cimport completion_queue +from grpc._cython._cygrpc cimport records +from grpc._cython._cygrpc cimport server + +from grpc._cython._cygrpc import call +from grpc._cython._cygrpc import channel +from grpc._cython._cygrpc import credentials +from grpc._cython._cygrpc import completion_queue +from grpc._cython._cygrpc import records +from grpc._cython._cygrpc import server + +StatusCode = records.StatusCode +CallError = records.CallError +CompletionType = records.CompletionType +OperationType = records.OperationType +Timespec = records.Timespec +CallDetails = records.CallDetails +Event = records.Event +ByteBuffer = records.ByteBuffer +SslPemKeyCertPair = records.SslPemKeyCertPair +ChannelArg = records.ChannelArg +ChannelArgs = records.ChannelArgs +Metadatum = records.Metadatum +Metadata = records.Metadata +Operation = records.Operation + +operation_send_initial_metadata = records.operation_send_initial_metadata +operation_send_message = records.operation_send_message +operation_send_close_from_client = records.operation_send_close_from_client +operation_send_status_from_server = records.operation_send_status_from_server +operation_receive_initial_metadata = records.operation_receive_initial_metadata +operation_receive_message = records.operation_receive_message +operation_receive_status_on_client = records.operation_receive_status_on_client +operation_receive_close_on_server = records.operation_receive_close_on_server + +Operations = records.Operations + +ClientCredentials = credentials.ClientCredentials +ServerCredentials = credentials.ServerCredentials + +client_credentials_google_default = ( + credentials.client_credentials_google_default) +client_credentials_ssl = credentials.client_credentials_ssl +client_credentials_composite_credentials = ( + credentials.client_credentials_composite_credentials) +client_credentials_compute_engine = ( + credentials.client_credentials_compute_engine) +client_credentials_jwt = credentials.client_credentials_jwt +client_credentials_refresh_token = credentials.client_credentials_refresh_token +client_credentials_iam = credentials.client_credentials_iam +server_credentials_ssl = credentials.server_credentials_ssl + +CompletionQueue = completion_queue.CompletionQueue +Channel = channel.Channel +Server = server.Server +Call = call.Call + + +# +# Global state +# + +cdef class _ModuleState: + + def __cinit__(self): + grpc.grpc_init() + + def __dealloc__(self): + grpc.grpc_shutdown() + +_module_state = _ModuleState() + diff --git a/src/python/grpcio/grpc/_cython/cygrpc_test.py b/src/python/grpcio/grpc/_cython/cygrpc_test.py new file mode 100644 index 0000000000..22d210b16b --- /dev/null +++ b/src/python/grpcio/grpc/_cython/cygrpc_test.py @@ -0,0 +1,262 @@ +# 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 time +import unittest + +from grpc._cython import cygrpc +from grpc._cython import test_utilities + + +class TypeSmokeTest(unittest.TestCase): + + def testStringsInUtilitiesUpDown(self): + self.assertEqual(0, cygrpc.StatusCode.ok) + metadatum = cygrpc.Metadatum('a', 'b') + self.assertEqual('a'.encode(), metadatum.key) + self.assertEqual('b'.encode(), metadatum.value) + metadata = cygrpc.Metadata([metadatum]) + self.assertEqual(1, len(metadata)) + self.assertEqual(metadatum.key, metadata[0].key) + + def testMetadataIteration(self): + metadata = cygrpc.Metadata([ + cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) + iterator = iter(metadata) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'a'.encode()) + self.assertEqual(metadatum.value, 'b'.encode()) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'c'.encode()) + self.assertEqual(metadatum.value, 'd'.encode()) + with self.assertRaises(StopIteration): + next(iterator) + + def testOperationsIteration(self): + operations = cygrpc.Operations([ + cygrpc.operation_send_message('asdf')]) + iterator = iter(operations) + operation = next(iterator) + self.assertIsInstance(operation, cygrpc.Operation) + # `Operation`s are write-only structures; can't directly debug anything out + # of them. Just check that we stop iterating. + with self.assertRaises(StopIteration): + next(iterator) + + def testTimespec(self): + now = time.time() + timespec = cygrpc.Timespec(now) + self.assertAlmostEqual(now, float(timespec), places=8) + + def testCompletionQueueUpDown(self): + completion_queue = cygrpc.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + server = cygrpc.Server(cygrpc.ChannelArgs([])) + del server + + def testChannelUpDown(self): + channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) + del channel + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartNoExplicitShutdown(self): + server = cygrpc.Server() + completion_queue = cygrpc.CompletionQueue() + server.register_completion_queue(completion_queue) + port = server.add_http2_port('[::]:0') + self.assertIsInstance(port, int) + server.start() + del server + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartShutdown(self): + completion_queue = cygrpc.CompletionQueue() + server = cygrpc.Server() + server.add_http2_port('[::]:0') + server.register_completion_queue(completion_queue) + server.start() + shutdown_tag = object() + server.shutdown(completion_queue, shutdown_tag) + event = completion_queue.poll() + self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) + self.assertIs(shutdown_tag, event.tag) + del server + del completion_queue + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = cygrpc.CompletionQueue() + self.server = cygrpc.Server() + self.server.register_completion_queue(self.server_completion_queue) + self.port = self.server.add_http2_port('[::]:0') + self.server.start() + self.client_completion_queue = cygrpc.CompletionQueue() + self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) + + def tearDown(self): + del self.server + 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 = b'key' + CLIENT_METADATA_ASCII_VALUE = b'val' + CLIENT_METADATA_BIN_KEY = b'key-bin' + CLIENT_METADATA_BIN_VALUE = b'\0'*1000 + SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' + SERVER_INITIAL_METADATA_VALUE = b'whodawha?' + SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' + SERVER_TRAILING_METADATA_VALUE = b'zomg it is' + SERVER_STATUS_CODE = cygrpc.StatusCode.ok + SERVER_STATUS_DETAILS = b'our work is never over' + REQUEST = b'in death a member of project mayhem has a name' + RESPONSE = b'his name is robert paulson' + METHOD = b'twinkies' + HOST = b'hostess' + + cygrpc_deadline = cygrpc.Timespec(DEADLINE) + + server_request_tag = object() + request_call_result = self.server.request_call( + self.server_completion_queue, self.server_completion_queue, + server_request_tag) + + self.assertEqual(cygrpc.CallError.ok, request_call_result) + + client_call_tag = object() + client_call = self.client_channel.create_call(self.client_completion_queue, + METHOD, HOST, cygrpc_deadline) + client_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, + CLIENT_METADATA_ASCII_VALUE), + cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) + client_start_batch_result = client_call.start_batch(cygrpc.Operations([ + cygrpc.operation_send_initial_metadata(client_initial_metadata), + cygrpc.operation_send_message(REQUEST), + cygrpc.operation_send_close_from_client(), + cygrpc.operation_receive_initial_metadata(), + cygrpc.operation_receive_message(), + cygrpc.operation_receive_status_on_client() + ]), client_call_tag) + self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) + client_event_future = test_utilities.CompletionQueuePollFuture( + self.client_completion_queue, cygrpc_deadline) + + request_event = self.server_completion_queue.poll(cygrpc_deadline) + self.assertEqual(cygrpc.CompletionType.operation_complete, + request_event.type) + self.assertIsInstance(request_event.operation_call, cygrpc.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(0, len(request_event.batch_operations)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.request_metadata)) + self.assertEqual(METHOD, request_event.request_call_details.method) + self.assertEqual(HOST, request_event.request_call_details.host) + self.assertLess( + abs(DEADLINE - float(request_event.request_call_details.deadline)), + DEADLINE_TOLERANCE) + + server_call_tag = object() + server_call = request_event.operation_call + server_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, + SERVER_INITIAL_METADATA_VALUE)]) + server_trailing_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, + SERVER_TRAILING_METADATA_VALUE)]) + server_start_batch_result = server_call.start_batch([ + cygrpc.operation_send_initial_metadata(server_initial_metadata), + cygrpc.operation_receive_message(), + cygrpc.operation_send_message(RESPONSE), + cygrpc.operation_receive_close_on_server(), + cygrpc.operation_send_status_from_server( + server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) + ], server_call_tag) + self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) + + client_event = client_event_future.result() + server_event = self.server_completion_queue.poll(cygrpc_deadline) + + self.assertEqual(6, len(client_event.batch_operations)) + found_client_op_types = set() + for client_result in client_event.batch_operations: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == cygrpc.OperationType.receive_initial_metadata: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.received_metadata)) + elif client_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(RESPONSE, client_result.received_message.bytes()) + elif client_result.type == cygrpc.OperationType.receive_status_on_client: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.received_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, + client_result.received_status_details) + self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.send_message, + cygrpc.OperationType.send_close_from_client, + cygrpc.OperationType.receive_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.receive_status_on_client + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.batch_operations)) + found_server_op_types = set() + for server_result in server_event.batch_operations: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(REQUEST, server_result.received_message.bytes()) + elif server_result.type == cygrpc.OperationType.receive_close_on_server: + self.assertFalse(server_result.received_cancelled) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.send_message, + cygrpc.OperationType.receive_close_on_server, + cygrpc.OperationType.send_status_from_server + ]), found_server_op_types) + + del client_call + del server_call + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_cython/test_utilities.py b/src/python/grpcio/grpc/_cython/test_utilities.py new file mode 100644 index 0000000000..21ea3075b4 --- /dev/null +++ b/src/python/grpcio/grpc/_cython/test_utilities.py @@ -0,0 +1,46 @@ +# 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 threading + +from grpc._cython._cygrpc import completion_queue + + +class CompletionQueuePollFuture: + + def __init__(self, completion_queue, deadline): + def poller_function(): + self._event_result = completion_queue.poll(deadline) + self._event_result = None + self._thread = threading.Thread(target=poller_function) + self._thread.start() + + def result(self): + self._thread.join() + return self._event_result diff --git a/src/python/grpcio/grpc/_junkdrawer/__init__.py b/src/python/grpcio/grpc/_junkdrawer/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/_junkdrawer/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/_junkdrawer/math_pb2.py b/src/python/grpcio/grpc/_junkdrawer/math_pb2.py new file mode 100644 index 0000000000..20165955b4 --- /dev/null +++ b/src/python/grpcio/grpc/_junkdrawer/math_pb2.py @@ -0,0 +1,266 @@ +# 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the math.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: math.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='math.proto', + package='math', + serialized_pb=_b('\n\nmath.proto\x12\x04math\",\n\x07\x44ivArgs\x12\x10\n\x08\x64ividend\x18\x01 \x02(\x03\x12\x0f\n\x07\x64ivisor\x18\x02 \x02(\x03\"/\n\x08\x44ivReply\x12\x10\n\x08quotient\x18\x01 \x02(\x03\x12\x11\n\tremainder\x18\x02 \x02(\x03\"\x18\n\x07\x46ibArgs\x12\r\n\x05limit\x18\x01 \x01(\x03\"\x12\n\x03Num\x12\x0b\n\x03num\x18\x01 \x02(\x03\"\x19\n\x08\x46ibReply\x12\r\n\x05\x63ount\x18\x01 \x02(\x03\x32\xa4\x01\n\x04Math\x12&\n\x03\x44iv\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00\x12.\n\x07\x44ivMany\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00(\x01\x30\x01\x12#\n\x03\x46ib\x12\r.math.FibArgs\x1a\t.math.Num\"\x00\x30\x01\x12\x1f\n\x03Sum\x12\t.math.Num\x1a\t.math.Num\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_DIVARGS = _descriptor.Descriptor( + name='DivArgs', + full_name='math.DivArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='dividend', full_name='math.DivArgs.dividend', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='divisor', full_name='math.DivArgs.divisor', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=64, +) + + +_DIVREPLY = _descriptor.Descriptor( + name='DivReply', + full_name='math.DivReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='quotient', full_name='math.DivReply.quotient', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='remainder', full_name='math.DivReply.remainder', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=66, + serialized_end=113, +) + + +_FIBARGS = _descriptor.Descriptor( + name='FibArgs', + full_name='math.FibArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='limit', full_name='math.FibArgs.limit', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=115, + serialized_end=139, +) + + +_NUM = _descriptor.Descriptor( + name='Num', + full_name='math.Num', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='num', full_name='math.Num.num', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=141, + serialized_end=159, +) + + +_FIBREPLY = _descriptor.Descriptor( + name='FibReply', + full_name='math.FibReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='count', full_name='math.FibReply.count', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=161, + serialized_end=186, +) + +DESCRIPTOR.message_types_by_name['DivArgs'] = _DIVARGS +DESCRIPTOR.message_types_by_name['DivReply'] = _DIVREPLY +DESCRIPTOR.message_types_by_name['FibArgs'] = _FIBARGS +DESCRIPTOR.message_types_by_name['Num'] = _NUM +DESCRIPTOR.message_types_by_name['FibReply'] = _FIBREPLY + +DivArgs = _reflection.GeneratedProtocolMessageType('DivArgs', (_message.Message,), dict( + DESCRIPTOR = _DIVARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivArgs) + )) +_sym_db.RegisterMessage(DivArgs) + +DivReply = _reflection.GeneratedProtocolMessageType('DivReply', (_message.Message,), dict( + DESCRIPTOR = _DIVREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivReply) + )) +_sym_db.RegisterMessage(DivReply) + +FibArgs = _reflection.GeneratedProtocolMessageType('FibArgs', (_message.Message,), dict( + DESCRIPTOR = _FIBARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibArgs) + )) +_sym_db.RegisterMessage(FibArgs) + +Num = _reflection.GeneratedProtocolMessageType('Num', (_message.Message,), dict( + DESCRIPTOR = _NUM, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.Num) + )) +_sym_db.RegisterMessage(Num) + +FibReply = _reflection.GeneratedProtocolMessageType('FibReply', (_message.Message,), dict( + DESCRIPTOR = _FIBREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibReply) + )) +_sym_db.RegisterMessage(FibReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py b/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py new file mode 100644 index 0000000000..eef18f82d6 --- /dev/null +++ b/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py @@ -0,0 +1,152 @@ +# 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the stock.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stock.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='stock.proto', + package='stock', + serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_STOCKREQUEST = _descriptor.Descriptor( + name='StockRequest', + full_name='stock.StockRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockRequest.symbol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=84, +) + + +_STOCKREPLY = _descriptor.Descriptor( + name='StockReply', + full_name='stock.StockReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='price', full_name='stock.StockReply.price', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockReply.symbol', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=86, + serialized_end=129, +) + +DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST +DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY + +StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( + DESCRIPTOR = _STOCKREQUEST, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockRequest) + )) +_sym_db.RegisterMessage(StockRequest) + +StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( + DESCRIPTOR = _STOCKREPLY, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockReply) + )) +_sym_db.RegisterMessage(StockReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio/grpc/_links/__init__.py b/src/python/grpcio/grpc/_links/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/_links/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py b/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py new file mode 100644 index 0000000000..3d629f4387 --- /dev/null +++ b/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py @@ -0,0 +1,88 @@ +# 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_cases +from grpc.framework.interfaces.links import test_utilities + +_NULL_BEHAVIOR = lambda unused_argument: None + + +class LonelyInvocationLinkTest(unittest.TestCase): + + def testUpAndDown(self): + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {}) + + invocation_link.start() + invocation_link.stop() + + def _test_lonely_invocation_with_termination(self, termination): + test_operation_id = object() + test_group = 'test package.Test Service' + test_method = 'test method' + invocation_link_mate = test_utilities.RecordingLink() + + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link( + channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, + {(test_group, test_method): _NULL_BEHAVIOR}) + invocation_link.join_link(invocation_link_mate) + invocation_link.start() + + ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, + None, None, None, None, termination) + invocation_link.accept_ticket(ticket) + invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + + self.assertIsNot( + invocation_link_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def testLonelyInvocationLinkWithCommencementTicket(self): + self._test_lonely_invocation_with_termination(None) + + def testLonelyInvocationLinkWithEntireTicket(self): + self._test_lonely_invocation_with_termination( + links.Ticket.Termination.COMPLETION) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/grpcio/grpc/_links/_proto_scenarios.py b/src/python/grpcio/grpc/_links/_proto_scenarios.py new file mode 100644 index 0000000000..320c0e0f50 --- /dev/null +++ b/src/python/grpcio/grpc/_links/_proto_scenarios.py @@ -0,0 +1,261 @@ +# 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc._junkdrawer import math_pb2 +from grpc.framework.common import test_constants + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def group_and_method(self): + """Access the test group and method. + + Returns: + The test group and method as a pair. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def group_and_method(self): + return 'math.Math', 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(test_constants.STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio/grpc/_links/_transmission_test.py b/src/python/grpcio/grpc/_links/_transmission_test.py new file mode 100644 index 0000000000..3eeec03f46 --- /dev/null +++ b/src/python/grpcio/grpc/_links/_transmission_test.py @@ -0,0 +1,231 @@ +# 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 transmission of tickets across gRPC-on-the-wire.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import _proto_scenarios +from grpc._links import invocation +from grpc._links import service +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_cases +from grpc.framework.interfaces.links import test_utilities + +_IDENTITY = lambda x: x + + +class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): + + def create_transmitting_links(self): + service_link = service.service_link( + {self.group_and_method(): self.deserialize_request}, + {self.group_and_method(): self.serialize_response}) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {self.group_and_method(): self.serialize_request}, + {self.group_and_method(): self.deserialize_response}) + invocation_link.start() + return invocation_link, service_link + + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + invocation_side_link.stop() + service_side_link.stop_gracefully() + + def create_invocation_initial_metadata(self): + return ( + ('first invocation initial metadata key', 'just a string value'), + ('second invocation initial metadata key', '0123456789'), + ('third invocation initial metadata key-bin', '\x00\x57' * 100), + ) + + def create_invocation_terminal_metadata(self): + return None + + def create_service_initial_metadata(self): + return ( + ('first service initial metadata key', 'just another string value'), + ('second service initial metadata key', '9876543210'), + ('third service initial metadata key-bin', '\x00\x59\x02' * 100), + ) + + def create_service_terminal_metadata(self): + return ( + ('first service terminal metadata key', 'yet another string value'), + ('second service terminal metadata key', 'abcdefghij'), + ('third service terminal metadata key-bin', '\x00\x37' * 100), + ) + + def create_invocation_completion(self): + return None, None + + def create_service_completion(self): + return _intermediary_low.Code.OK, 'An exuberant test "details" message!' + + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + # we need to filter out any additional metadata added in transmitted_metadata + # since implementations are allowed to add to what is sent (in any position) + keys, _ = zip(*original_metadata) + self.assertSequenceEqual( + original_metadata, + [x for x in transmitted_metadata if x[0] in keys]) + + +class RoundTripTest(unittest.TestCase): + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_group = 'test package.Test Group' + test_method = 'test method' + identity_transformation = {(test_group, test_method): _IDENTITY} + test_code = _intermediary_low.Code.OK + test_message = 'a test message' + + service_link = service.service_link( + identity_transformation, identity_transformation) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', identity_transformation, identity_transformation) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + + service_ticket = links.Ticket( + service_mate.tickets()[-1].operation_id, 0, None, None, None, None, + None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + self.assertIs( + service_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + self.assertIs( + invocation_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_group, test_method = scenario.group_and_method() + test_code = _intermediary_low.Code.OK + test_message = 'a scenario test message' + + service_link = service.service_link( + {(test_group, test_method): scenario.deserialize_request}, + {(test_group, test_method): scenario.serialize_response}) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {(test_group, test_method): scenario.serialize_request}, + {(test_group, test_method): scenario.deserialize_response}) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, None) + invocation_link.accept_ticket(invocation_ticket) + requests = scenario.requests() + for request_index, request in enumerate(requests): + request_ticket = links.Ticket( + test_operation_id, 1 + request_index, None, None, None, None, 1, None, + request, None, None, None, None) + invocation_link.accept_ticket(request_ticket) + service_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + response_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_index, None, None, + None, None, 1, None, scenario.response_for_request(request), None, + None, None, None) + service_link.accept_ticket(response_ticket) + invocation_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + request_count = len(requests) + invocation_completion_ticket = links.Ticket( + test_operation_id, request_count + 1, None, None, None, None, None, + None, None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_completion_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + service_completion_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_count, None, None, None, + None, None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_completion_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + observed_requests = tuple( + ticket.payload for ticket in service_mate.tickets() + if ticket.payload is not None) + observed_responses = tuple( + ticket.payload for ticket in invocation_mate.tickets() + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(observed_requests)) + self.assertTrue(scenario.verify_responses(observed_responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_links/invocation.py b/src/python/grpcio/grpc/_links/invocation.py new file mode 100644 index 0000000000..0058ae91f8 --- /dev/null +++ b/src/python/grpcio/grpc/_links/invocation.py @@ -0,0 +1,363 @@ +# 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. + +"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + + +@enum.unique +class _Read(enum.Enum): + AWAITING_METADATA = 'awaiting metadata' + READING = 'reading' + AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + + def __init__( + self, call, request_serializer, response_deserializer, sequence_number, + read, allowance, high_write, low_write): + self.call = call + self.request_serializer = request_serializer + self.response_deserializer = response_deserializer + self.sequence_number = sequence_number + self.read = read + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + + +class _Kernel(object): + + def __init__( + self, channel, host, request_serializers, response_deserializers, + ticket_relay): + self._lock = threading.Lock() + self._channel = channel + self._host = host + self._request_serializers = request_serializers + self._response_deserializers = response_deserializers + self._relay = ticket_relay + + self._completion_queue = None + self._rpc_states = None + self._pool = None + + def _on_write_event(self, operation_id, unused_event, rpc_state): + if rpc_state.high_write is _HighWrite.CLOSED: + rpc_state.call.complete(operation_id) + rpc_state.low_write = _LowWrite.CLOSED + else: + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, 1, + None, None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + + def _on_read_event(self, operation_id, event, rpc_state): + if event.bytes is None: + rpc_state.read = _Read.CLOSED + else: + if 0 < rpc_state.allowance: + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + else: + rpc_state.read = _Read.AWAITING_ALLOWANCE + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, rpc_state.response_deserializer(event.bytes), None, None, None, + None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_metadata_event(self, operation_id, event, rpc_state): + rpc_state.allowance -= 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, + links.Ticket.Subscription.FULL, None, None, event.metadata, None, None, + None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_finish_event(self, operation_id, event, rpc_state): + self._rpc_states.pop(operation_id, None) + if event.status.code is _intermediary_low.Code.OK: + termination = links.Ticket.Termination.COMPLETION + elif event.status.code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + ticket = links.Ticket( + operation_id, rpc_state.sequence_number, None, None, None, None, None, + None, None, event.metadata, event.status.code, event.status.details, + termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue): + while True: + event = completion_queue.get(None) + if event.kind is _intermediary_low.Event.Kind.STOP: + return + operation_id = event.tag + with self._lock: + if self._completion_queue is None: + continue + rpc_state = self._rpc_states.get(operation_id) + if rpc_state is not None: + if event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.METADATA_ACCEPTED: + self._on_metadata_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: + self._on_read_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.FINISH: + self._on_finish_event(operation_id, event, rpc_state) + elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: + pass + else: + logging.error('Illegal RPC event! %s', (event,)) + + def _invoke( + self, operation_id, group, method, initial_metadata, payload, termination, + timeout, allowance): + """Invoke an RPC. + + Args: + operation_id: Any object to be used as an operation ID for the RPC. + group: The group to which the RPC method belongs. + method: The RPC method name. + initial_metadata: The initial metadata object for the RPC. + payload: A payload object for the RPC or None if no payload was given at + invocation-time. + termination: A links.Ticket.Termination value or None indicated whether or + not more writes will follow from this side of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + allowance: The number of payloads (beyond the free first one) that the + local ticket exchange mate has granted permission to be read. + """ + if termination is links.Ticket.Termination.COMPLETION: + high_write = _HighWrite.CLOSED + elif termination is None: + high_write = _HighWrite.OPEN + else: + return + + request_serializer = self._request_serializers.get((group, method)) + response_deserializer = self._response_deserializers.get((group, method)) + if request_serializer is None or response_deserializer is None: + cancellation_ticket = links.Ticket( + operation_id, 0, None, None, None, None, None, None, None, None, None, + None, links.Ticket.Termination.CANCELLATION) + self._relay.add_value(cancellation_ticket) + return + + call = _intermediary_low.Call( + self._channel, self._completion_queue, '/%s/%s' % (group, method), + self._host, time.time() + timeout) + if initial_metadata is not None: + for metadata_key, metadata_value in initial_metadata: + call.add_metadata(metadata_key, metadata_value) + call.invoke(self._completion_queue, operation_id, operation_id) + if payload is None: + if high_write is _HighWrite.CLOSED: + call.complete(operation_id) + low_write = _LowWrite.CLOSED + else: + low_write = _LowWrite.OPEN + else: + call.write(request_serializer(payload), operation_id) + low_write = _LowWrite.ACTIVE + self._rpc_states[operation_id] = _RPCState( + call, request_serializer, response_deserializer, 0, + _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), + high_write, low_write) + + def _advance(self, operation_id, rpc_state, payload, termination, allowance): + if payload is not None: + rpc_state.call.write(rpc_state.request_serializer(payload), operation_id) + rpc_state.low_write = _LowWrite.ACTIVE + + if allowance is not None: + if rpc_state.read is _Read.AWAITING_ALLOWANCE: + rpc_state.allowance += allowance - 1 + rpc_state.call.read(operation_id) + rpc_state.read = _Read.READING + else: + rpc_state.allowance += allowance + + if termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + rpc_state.call.complete(operation_id) + rpc_state.low_write = _LowWrite.CLOSED + elif termination is not None: + rpc_state.call.cancel() + + def add_ticket(self, ticket): + with self._lock: + if self._completion_queue is None: + return + if ticket.sequence_number == 0: + self._invoke( + ticket.operation_id, ticket.group, ticket.method, + ticket.initial_metadata, ticket.payload, ticket.termination, + ticket.timeout, ticket.allowance) + else: + rpc_state = self._rpc_states.get(ticket.operation_id) + if rpc_state is not None: + self._advance( + ticket.operation_id, rpc_state, ticket.payload, + ticket.termination, ticket.allowance) + + def start(self): + """Starts this object. + + This method must be called before attempting to exchange tickets with this + object. + """ + with self._lock: + self._completion_queue = _intermediary_low.CompletionQueue() + self._rpc_states = {} + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue) + + def stop(self): + """Stops this object. + + This method must be called for proper termination of this object, and no + attempts to exchange tickets with this object may be made after this method + has been called. + """ + with self._lock: + self._completion_queue.stop() + self._completion_queue = None + pool = self._pool + self._pool = None + self._rpc_states = None + pool.shutdown(wait=True) + + +class InvocationLink(links.Link, activated.Activated): + """A links.Link for use on the invocation-side of a gRPC connection. + + Implementations of this interface are only valid for use when activated. + """ + __metaclass__ = abc.ABCMeta + + +class _InvocationLink(InvocationLink): + + def __init__( + self, channel, host, request_serializers, response_deserializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + channel, host, request_serializers, response_deserializers, self._relay) + + def _start(self): + self._relay.start() + self._kernel.start() + return self + + def _stop(self): + self._kernel.stop() + self._relay.stop() + + def accept_ticket(self, ticket): + """See links.Link.accept_ticket for specification.""" + self._kernel.add_ticket(ticket) + + def join_link(self, link): + """See links.Link.join_link for specification.""" + self._relay.set_behavior(link.accept_ticket) + + def __enter__(self): + """See activated.Activated.__enter__ for specification.""" + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + """See activated.Activated.__exit__ for specification.""" + self._stop() + return False + + def start(self): + """See activated.Activated.start for specification.""" + return self._start() + + def stop(self): + """See activated.Activated.stop for specification.""" + self._stop() + + +def invocation_link(channel, host, request_serializers, response_deserializers): + """Creates an InvocationLink. + + Args: + channel: A channel for use by the link. + host: The host to specify when invoking RPCs. + request_serializers: A dict from group-method pair to request object + serialization behavior. + response_deserializers: A dict from group-method pair to response object + deserialization behavior. + + Returns: + An InvocationLink. + """ + return _InvocationLink( + channel, host, request_serializers, response_deserializers) diff --git a/src/python/grpcio/grpc/_links/service.py b/src/python/grpcio/grpc/_links/service.py new file mode 100644 index 0000000000..7783e91824 --- /dev/null +++ b/src/python/grpcio/grpc/_links/service.py @@ -0,0 +1,402 @@ +# 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. + +"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" + +import abc +import enum +import logging +import threading +import time + +from grpc._adapter import _intermediary_low +from grpc.framework.foundation import logging_pool +from grpc.framework.foundation import relay +from grpc.framework.interfaces.links import links + + +@enum.unique +class _Read(enum.Enum): + READING = 'reading' + AWAITING_ALLOWANCE = 'awaiting allowance' + CLOSED = 'closed' + + +@enum.unique +class _HighWrite(enum.Enum): + OPEN = 'open' + CLOSED = 'closed' + + +@enum.unique +class _LowWrite(enum.Enum): + """The possible categories of low-level write state.""" + + OPEN = 'OPEN' + ACTIVE = 'ACTIVE' + CLOSED = 'CLOSED' + + +class _RPCState(object): + + def __init__( + self, request_deserializer, response_serializer, sequence_number, read, + allowance, high_write, low_write, premetadataed, terminal_metadata, code, + message): + self.request_deserializer = request_deserializer + self.response_serializer = response_serializer + self.sequence_number = sequence_number + self.read = read + self.allowance = allowance + self.high_write = high_write + self.low_write = low_write + self.premetadataed = premetadataed + self.terminal_metadata = terminal_metadata + self.code = code + self.message = message + + +def _metadatafy(call, metadata): + for metadata_key, metadata_value in metadata: + call.add_metadata(metadata_key, metadata_value) + + +class _Kernel(object): + + def __init__(self, request_deserializers, response_serializers, ticket_relay): + self._lock = threading.Lock() + self._request_deserializers = request_deserializers + self._response_serializers = response_serializers + self._relay = ticket_relay + + self._completion_queue = None + self._server = None + self._rpc_states = {} + self._pool = None + + def _on_service_acceptance_event(self, event, server): + server.service(None) + + service_acceptance = event.service_acceptance + call = service_acceptance.call + call.accept(self._completion_queue, call) + try: + group, method = service_acceptance.method.split('/')[1:3] + except ValueError: + logging.info('Illegal path "%s"!', service_acceptance.method) + return + request_deserializer = self._request_deserializers.get((group, method)) + response_serializer = self._response_serializers.get((group, method)) + if request_deserializer is None or response_serializer is None: + # TODO(nathaniel): Terminate the RPC with code NOT_FOUND. + call.cancel() + return + + call.read(call) + self._rpc_states[call] = _RPCState( + request_deserializer, response_serializer, 1, _Read.READING, 0, + _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None) + ticket = links.Ticket( + call, 0, group, method, links.Ticket.Subscription.FULL, + service_acceptance.deadline - time.time(), None, event.metadata, None, + None, None, None, None) + self._relay.add_value(ticket) + + def _on_read_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if event.bytes is None: + rpc_state.read = _Read.CLOSED + payload = None + termination = links.Ticket.Termination.COMPLETION + else: + if 0 < rpc_state.allowance: + rpc_state.allowance -= 1 + call.read(call) + else: + rpc_state.read = _Read.AWAITING_ALLOWANCE + payload = rpc_state.request_deserializer(event.bytes) + termination = None + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + payload, None, None, None, termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _on_write_event(self, event): + call = event.tag + rpc_state = self._rpc_states.get(call, None) + if rpc_state is None: + return + + if rpc_state.high_write is _HighWrite.CLOSED: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + call.status( + _intermediary_low.Status(rpc_state.code, rpc_state.message), call) + rpc_state.low_write = _LowWrite.CLOSED + else: + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, 1, None, + None, None, None, None, None) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + rpc_state.low_write = _LowWrite.OPEN + + def _on_finish_event(self, event): + call = event.tag + rpc_state = self._rpc_states.pop(call, None) + if rpc_state is None: + return + code = event.status.code + if code is _intermediary_low.Code.OK: + return + + if code is _intermediary_low.Code.CANCELLED: + termination = links.Ticket.Termination.CANCELLATION + elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: + termination = links.Ticket.Termination.EXPIRATION + else: + termination = links.Ticket.Termination.TRANSMISSION_FAILURE + ticket = links.Ticket( + call, rpc_state.sequence_number, None, None, None, None, None, None, + None, None, None, None, termination) + rpc_state.sequence_number += 1 + self._relay.add_value(ticket) + + def _spin(self, completion_queue, server): + while True: + event = completion_queue.get(None) + if event.kind is _intermediary_low.Event.Kind.STOP: + return + with self._lock: + if self._server is None: + continue + elif event.kind is _intermediary_low.Event.Kind.SERVICE_ACCEPTED: + self._on_service_acceptance_event(event, server) + elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: + self._on_read_event(event) + elif event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: + self._on_write_event(event) + elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: + pass + elif event.kind is _intermediary_low.Event.Kind.FINISH: + self._on_finish_event(event) + else: + logging.error('Illegal event! %s', (event,)) + + def add_ticket(self, ticket): + with self._lock: + if self._server is None: + return + call = ticket.operation_id + rpc_state = self._rpc_states.get(call) + if rpc_state is None: + return + + if ticket.initial_metadata is not None: + _metadatafy(call, ticket.initial_metadata) + call.premetadata() + rpc_state.premetadataed = True + elif not rpc_state.premetadataed: + if (ticket.terminal_metadata is not None or + ticket.payload is not None or + ticket.termination is links.Ticket.Termination.COMPLETION or + ticket.code is not None or + ticket.message is not None): + call.premetadata() + rpc_state.premetadataed = True + + if ticket.allowance is not None: + if rpc_state.read is _Read.AWAITING_ALLOWANCE: + rpc_state.allowance += ticket.allowance - 1 + call.read(call) + rpc_state.read = _Read.READING + else: + rpc_state.allowance += ticket.allowance + + if ticket.payload is not None: + call.write(rpc_state.response_serializer(ticket.payload), call) + rpc_state.low_write = _LowWrite.ACTIVE + + if ticket.terminal_metadata is not None: + rpc_state.terminal_metadata = ticket.terminal_metadata + if ticket.code is not None: + rpc_state.code = ticket.code + if ticket.message is not None: + rpc_state.message = ticket.message + + if ticket.termination is links.Ticket.Termination.COMPLETION: + rpc_state.high_write = _HighWrite.CLOSED + if rpc_state.low_write is _LowWrite.OPEN: + if rpc_state.terminal_metadata is not None: + _metadatafy(call, rpc_state.terminal_metadata) + status = _intermediary_low.Status( + _intermediary_low.Code.OK + if rpc_state.code is None else rpc_state.code, + '' if rpc_state.message is None else rpc_state.message) + call.status(status, call) + rpc_state.low_write = _LowWrite.CLOSED + elif ticket.termination is not None: + call.cancel() + self._rpc_states.pop(call, None) + + def add_port(self, port, server_credentials): + with self._lock: + address = '[::]:%d' % port + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + if server_credentials is None: + return self._server.add_http2_addr(address) + else: + return self._server.add_secure_http2_addr(address, server_credentials) + + def start(self): + with self._lock: + if self._server is None: + self._completion_queue = _intermediary_low.CompletionQueue() + self._server = _intermediary_low.Server(self._completion_queue) + self._pool = logging_pool.pool(1) + self._pool.submit(self._spin, self._completion_queue, self._server) + self._server.start() + self._server.service(None) + + def graceful_stop(self): + with self._lock: + self._server.stop() + self._server = None + self._completion_queue.stop() + self._completion_queue = None + pool = self._pool + self._pool = None + self._rpc_states = None + pool.shutdown(wait=True) + + def immediate_stop(self): + # TODO(nathaniel): Implementation. + raise NotImplementedError( + 'TODO(nathaniel): after merge of rewritten lower layers') + + +class ServiceLink(links.Link): + """A links.Link for use on the service-side of a gRPC connection. + + Implementations of this interface are only valid for use between calls to + their start method and one of their stop methods. + """ + + @abc.abstractmethod + def add_port(self, port, server_credentials): + """Adds a port on which to service RPCs after this link has been started. + + Args: + port: The port on which to service RPCs, or zero to request that a port be + automatically selected and used. + server_credentials: A ServerCredentials object, or None for insecure + service. + + Returns: + A port on which RPCs will be serviced after this link has been started. + """ + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Starts this object. + + This method must be called before attempting to use this Link in ticket + exchange. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop_gracefully(self): + """Stops this link. + + New RPCs will be rejected as soon as this method is called, but ongoing RPCs + will be allowed to continue until they terminate. This method blocks until + all RPCs have terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop_immediately(self): + """Stops this link. + + All in-progress RPCs will be terminated immediately. + """ + raise NotImplementedError() + + +class _ServiceLink(ServiceLink): + + def __init__(self, request_deserializers, response_serializers): + self._relay = relay.relay(None) + self._kernel = _Kernel( + request_deserializers, response_serializers, self._relay) + + def accept_ticket(self, ticket): + self._kernel.add_ticket(ticket) + + def join_link(self, link): + self._relay.set_behavior(link.accept_ticket) + + def add_port(self, port, server_credentials): + return self._kernel.add_port(port, server_credentials) + + def start(self): + self._relay.start() + return self._kernel.start() + + def stop_gracefully(self): + self._kernel.graceful_stop() + self._relay.stop() + + def stop_immediately(self): + self._kernel.immediate_stop() + self._relay.stop() + + +def service_link(request_deserializers, response_serializers): + """Creates a ServiceLink. + + Args: + request_deserializers: A dict from group-method pair to request object + deserialization behavior. + response_serializers: A dict from group-method pair to response ojbect + serialization behavior. + + Returns: + A ServiceLink. + """ + return _ServiceLink(request_deserializers, response_serializers) diff --git a/src/python/grpcio/grpc/early_adopter/__init__.py b/src/python/grpcio/grpc/early_adopter/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/early_adopter/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/early_adopter/implementations.py b/src/python/grpcio/grpc/early_adopter/implementations.py new file mode 100644 index 0000000000..10919fae69 --- /dev/null +++ b/src/python/grpcio/grpc/early_adopter/implementations.py @@ -0,0 +1,250 @@ +# 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. + +"""Entry points into GRPC.""" + +import threading + +from grpc._adapter import fore as _fore +from grpc._adapter import rear as _rear +from grpc.framework.alpha import _face_utilities +from grpc.framework.alpha import _reexport +from grpc.framework.alpha import interfaces +from grpc.framework.base import implementations as _base_implementations +from grpc.framework.base import util as _base_utilities +from grpc.framework.face import implementations as _face_implementations +from grpc.framework.foundation import logging_pool + +_THREAD_POOL_SIZE = 8 +_ONE_DAY_IN_SECONDS = 24 * 60 * 60 + + +class _Server(interfaces.Server): + + def __init__(self, breakdown, port, private_key, certificate_chain): + self._lock = threading.Lock() + self._breakdown = breakdown + self._port = port + if private_key is None or certificate_chain is None: + self._key_chain_pairs = () + else: + self._key_chain_pairs = ((private_key, certificate_chain),) + + self._pool = None + self._back = None + self._fore_link = None + + def _start(self): + with self._lock: + if self._pool is None: + self._pool = logging_pool.pool(_THREAD_POOL_SIZE) + servicer = _face_implementations.servicer( + self._pool, self._breakdown.implementations, None) + self._back = _base_implementations.back_link( + servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS, + _ONE_DAY_IN_SECONDS) + self._fore_link = _fore.ForeLink( + self._pool, self._breakdown.request_deserializers, + self._breakdown.response_serializers, None, self._key_chain_pairs, + port=self._port) + self._back.join_fore_link(self._fore_link) + self._fore_link.join_rear_link(self._back) + self._fore_link.start() + else: + raise ValueError('Server currently running!') + + def _stop(self): + with self._lock: + if self._pool is None: + raise ValueError('Server not running!') + else: + self._fore_link.stop() + _base_utilities.wait_for_idle(self._back) + self._pool.shutdown(wait=True) + self._fore_link = None + self._back = None + self._pool = None + + def __enter__(self): + self._start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop() + return False + + def start(self): + self._start() + + def stop(self): + self._stop() + + def port(self): + with self._lock: + return self._fore_link.port() + + +class _Stub(interfaces.Stub): + + def __init__( + self, breakdown, host, port, secure, root_certificates, private_key, + certificate_chain, metadata_transformer=None, server_host_override=None): + self._lock = threading.Lock() + self._breakdown = breakdown + self._host = host + self._port = port + self._secure = secure + self._root_certificates = root_certificates + self._private_key = private_key + self._certificate_chain = certificate_chain + self._metadata_transformer = metadata_transformer + self._server_host_override = server_host_override + + self._pool = None + self._front = None + self._rear_link = None + self._understub = None + + def __enter__(self): + with self._lock: + if self._pool is None: + self._pool = logging_pool.pool(_THREAD_POOL_SIZE) + self._front = _base_implementations.front_link( + self._pool, self._pool, self._pool) + self._rear_link = _rear.RearLink( + self._host, self._port, self._pool, + self._breakdown.request_serializers, + self._breakdown.response_deserializers, self._secure, + self._root_certificates, self._private_key, self._certificate_chain, + metadata_transformer=self._metadata_transformer, + server_host_override=self._server_host_override) + self._front.join_rear_link(self._rear_link) + self._rear_link.join_fore_link(self._front) + self._rear_link.start() + self._understub = _face_implementations.dynamic_stub( + self._breakdown.face_cardinalities, self._front, self._pool, '') + else: + raise ValueError('Tried to __enter__ already-__enter__ed Stub!') + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + with self._lock: + if self._pool is None: + raise ValueError('Tried to __exit__ non-__enter__ed Stub!') + else: + self._rear_link.stop() + _base_utilities.wait_for_idle(self._front) + self._pool.shutdown(wait=True) + self._rear_link = None + self._front = None + self._pool = None + self._understub = None + return False + + def __getattr__(self, attr): + with self._lock: + if self._pool is None: + raise ValueError('Tried to __getattr__ non-__enter__ed Stub!') + else: + method_cardinality = self._breakdown.cardinalities.get(attr) + underlying_attr = getattr( + self._understub, self._breakdown.qualified_names.get(attr), None) + if method_cardinality is interfaces.Cardinality.UNARY_UNARY: + return _reexport.unary_unary_sync_async(underlying_attr) + elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: + return lambda request, timeout: _reexport.cancellable_iterator( + underlying_attr(request, timeout)) + elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: + return _reexport.stream_unary_sync_async(underlying_attr) + elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: + return lambda request_iterator, timeout: ( + _reexport.cancellable_iterator(underlying_attr( + request_iterator, timeout))) + else: + raise AttributeError(attr) + + +def stub( + service_name, methods, host, port, metadata_transformer=None, secure=False, + root_certificates=None, private_key=None, certificate_chain=None, + server_host_override=None): + """Constructs an interfaces.Stub. + + Args: + service_name: The package-qualified full name of the service. + methods: A dictionary from RPC method name to + interfaces.RpcMethodInvocationDescription describing the RPCs to be + supported by the created stub. The RPC method names in the dictionary are + not qualified by the service name or decorated in any other way. + host: The host to which to connect for RPC service. + port: The port to which to connect for RPC service. + metadata_transformer: A callable that given a metadata object produces + another metadata object to be used in the underlying communication on the + wire. + secure: Whether or not to construct the stub with a secure connection. + root_certificates: The PEM-encoded root certificates or None to ask for + them to be retrieved from a default location. + private_key: The PEM-encoded private key to use or None if no private key + should be used. + certificate_chain: The PEM-encoded certificate chain to use or None if no + certificate chain should be used. + server_host_override: (For testing only) the target name used for SSL + host name checking. + + Returns: + An interfaces.Stub affording RPC invocation. + """ + breakdown = _face_utilities.break_down_invocation(service_name, methods) + return _Stub( + breakdown, host, port, secure, root_certificates, private_key, + certificate_chain, server_host_override=server_host_override, + metadata_transformer=metadata_transformer) + + +def server( + service_name, methods, port, private_key=None, certificate_chain=None): + """Constructs an interfaces.Server. + + Args: + service_name: The package-qualified full name of the service. + methods: A dictionary from RPC method name to + interfaces.RpcMethodServiceDescription describing the RPCs to + be serviced by the created server. The RPC method names in the dictionary + are not qualified by the service name or decorated in any other way. + port: The port on which to serve or zero to ask for a port to be + automatically selected. + private_key: A pem-encoded private key, or None for an insecure server. + certificate_chain: A pem-encoded certificate chain, or None for an insecure + server. + + Returns: + An interfaces.Server that will serve secure traffic. + """ + breakdown = _face_utilities.break_down_service(service_name, methods) + return _Server(breakdown, port, private_key, certificate_chain) diff --git a/src/python/grpcio/grpc/early_adopter/implementations_test.py b/src/python/grpcio/grpc/early_adopter/implementations_test.py new file mode 100644 index 0000000000..49f0e949c4 --- /dev/null +++ b/src/python/grpcio/grpc/early_adopter/implementations_test.py @@ -0,0 +1,180 @@ +# 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. + +# TODO(nathaniel): Expand this test coverage. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import unittest + +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +from grpc._junkdrawer import math_pb2 + +SERVICE_NAME = 'math.Math' + +DIV = 'Div' +DIV_MANY = 'DivMany' +FIB = 'Fib' +SUM = 'Sum' + +def _fibbonacci(limit): + left, right = 0, 1 + for _ in xrange(limit): + yield left + left, right = right, left + right + + +def _div(request, unused_context): + return math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _div_many(request_iterator, unused_context): + for request in request_iterator: + yield math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _fib(request, unused_context): + for number in _fibbonacci(request.limit): + yield math_pb2.Num(num=number) + + +def _sum(request_iterator, unused_context): + accumulation = 0 + for request in request_iterator: + accumulation += request.num + return math_pb2.Num(num=accumulation) + + +_INVOCATION_DESCRIPTIONS = { + DIV: utilities.unary_unary_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + DIV_MANY: utilities.stream_stream_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + FIB: utilities.unary_stream_invocation_description( + math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), + SUM: utilities.stream_unary_invocation_description( + math_pb2.Num.SerializeToString, math_pb2.Num.FromString), +} + +_SERVICE_DESCRIPTIONS = { + DIV: utilities.unary_unary_service_description( + _div, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + DIV_MANY: utilities.stream_stream_service_description( + _div_many, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + FIB: utilities.unary_stream_service_description( + _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), + SUM: utilities.stream_unary_service_description( + _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), +} + +_TIMEOUT = 3 + + +class EarlyAdopterImplementationsTest(unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) + + def tearDown(self): + self.server.stop() + + def testUpAndDown(self): + with self.stub: + pass + + def testUnaryUnary(self): + divisor = 59 + dividend = 973 + expected_quotient = dividend / divisor + expected_remainder = dividend % divisor + + with self.stub: + response = self.stub.Div( + math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) + self.assertEqual(expected_quotient, response.quotient) + self.assertEqual(expected_remainder, response.remainder) + + def testUnaryStream(self): + stream_length = 43 + + with self.stub: + response_iterator = self.stub.Fib( + math_pb2.FibArgs(limit=stream_length), _TIMEOUT) + numbers = tuple(response.num for response in response_iterator) + for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): + self.assertEqual(early + middle, later) + self.assertEqual(stream_length, len(numbers)) + + def testStreamUnary(self): + stream_length = 127 + + with self.stub: + response_future = self.stub.Sum.async( + (math_pb2.Num(num=index) for index in range(stream_length)), + _TIMEOUT) + self.assertEqual( + (stream_length * (stream_length - 1)) / 2, + response_future.result().num) + + def testStreamStream(self): + stream_length = 179 + divisor_offset = 71 + dividend_offset = 1763 + + with self.stub: + response_iterator = self.stub.DivMany( + (math_pb2.DivArgs( + divisor=divisor_offset + index, + dividend=dividend_offset + index) + for index in range(stream_length)), + _TIMEOUT) + for index, response in enumerate(response_iterator): + self.assertEqual( + (dividend_offset + index) / (divisor_offset + index), + response.quotient) + self.assertEqual( + (dividend_offset + index) % (divisor_offset + index), + response.remainder) + self.assertEqual(stream_length, index + 1) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/__init__.py b/src/python/grpcio/grpc/framework/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/alpha/__init__.py b/src/python/grpcio/grpc/framework/alpha/__init__.py new file mode 100644 index 0000000000..b89398809f --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/__init__.py @@ -0,0 +1,28 @@ +# 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. diff --git a/src/python/grpcio/grpc/framework/alpha/_face_utilities.py b/src/python/grpcio/grpc/framework/alpha/_face_utilities.py new file mode 100644 index 0000000000..fb0cfe426d --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/_face_utilities.py @@ -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. + +import abc +import collections + +# face_interfaces is referenced from specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc.framework.face import utilities as face_utilities +from grpc.framework.alpha import _reexport +from grpc.framework.alpha import interfaces + + +def _qualified_name(service_name, method_name): + return '/%s/%s' % (service_name, method_name) + + +# TODO(nathaniel): This structure is getting bloated; it could be shrunk if +# implementations._Stub used a generic rather than a dynamic underlying +# face-layer stub. +class InvocationBreakdown(object): + """An intermediate representation of invocation-side views of RPC methods. + + Attributes: + cardinalities: A dictionary from RPC method name to interfaces.Cardinality + value. + qualified_names: A dictionary from unqualified RPC method name to + service-qualified RPC method name. + face_cardinalities: A dictionary from service-qualified RPC method name to + to cardinality.Cardinality value. + request_serializers: A dictionary from service-qualified RPC method name to + callable behavior to be used serializing request values for the RPC. + response_deserializers: A dictionary from service-qualified RPC method name + to callable behavior to be used deserializing response values for the + RPC. + """ + __metaclass__ = abc.ABCMeta + + +class _EasyInvocationBreakdown( + InvocationBreakdown, + collections.namedtuple( + '_EasyInvocationBreakdown', + ('cardinalities', 'qualified_names', 'face_cardinalities', + 'request_serializers', 'response_deserializers'))): + pass + + +class ServiceBreakdown(object): + """An intermediate representation of service-side views of RPC methods. + + Attributes: + implementations: A dictionary from service-qualified RPC method name to + face_interfaces.MethodImplementation implementing the RPC method. + request_deserializers: A dictionary from service-qualified RPC method name + to callable behavior to be used deserializing request values for the RPC. + response_serializers: A dictionary from service-qualified RPC method name + to callable behavior to be used serializing response values for the RPC. + """ + __metaclass__ = abc.ABCMeta + + +class _EasyServiceBreakdown( + ServiceBreakdown, + collections.namedtuple( + '_EasyServiceBreakdown', + ('implementations', 'request_deserializers', 'response_serializers'))): + pass + + +def break_down_invocation(service_name, method_descriptions): + """Derives an InvocationBreakdown from several RPC method descriptions. + + Args: + service_name: The package-qualified full name of the service. + method_descriptions: A dictionary from RPC method name to + interfaces.RpcMethodInvocationDescription describing the RPCs. + + Returns: + An InvocationBreakdown corresponding to the given method descriptions. + """ + cardinalities = {} + qualified_names = {} + face_cardinalities = {} + request_serializers = {} + response_deserializers = {} + for name, method_description in method_descriptions.iteritems(): + qualified_name = _qualified_name(service_name, name) + method_cardinality = method_description.cardinality() + cardinalities[name] = method_description.cardinality() + qualified_names[name] = qualified_name + face_cardinalities[qualified_name] = _reexport.common_cardinality( + method_cardinality) + request_serializers[qualified_name] = method_description.serialize_request + response_deserializers[qualified_name] = ( + method_description.deserialize_response) + return _EasyInvocationBreakdown( + cardinalities, qualified_names, face_cardinalities, request_serializers, + response_deserializers) + + +def break_down_service(service_name, method_descriptions): + """Derives a ServiceBreakdown from several RPC method descriptions. + + Args: + method_descriptions: A dictionary from RPC method name to + interfaces.RpcMethodServiceDescription describing the RPCs. + + Returns: + A ServiceBreakdown corresponding to the given method descriptions. + """ + implementations = {} + request_deserializers = {} + response_serializers = {} + for name, method_description in method_descriptions.iteritems(): + qualified_name = _qualified_name(service_name, name) + method_cardinality = method_description.cardinality() + if method_cardinality is interfaces.Cardinality.UNARY_UNARY: + def service( + request, face_rpc_context, + service_behavior=method_description.service_unary_unary): + return service_behavior( + request, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.unary_unary_inline( + service) + elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: + def service( + request, face_rpc_context, + service_behavior=method_description.service_unary_stream): + return service_behavior( + request, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.unary_stream_inline( + service) + elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: + def service( + request_iterator, face_rpc_context, + service_behavior=method_description.service_stream_unary): + return service_behavior( + request_iterator, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.stream_unary_inline( + service) + elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: + def service( + request_iterator, face_rpc_context, + service_behavior=method_description.service_stream_stream): + return service_behavior( + request_iterator, _reexport.rpc_context(face_rpc_context)) + implementations[qualified_name] = face_utilities.stream_stream_inline( + service) + request_deserializers[qualified_name] = ( + method_description.deserialize_request) + response_serializers[qualified_name] = ( + method_description.serialize_response) + + return _EasyServiceBreakdown( + implementations, request_deserializers, response_serializers) diff --git a/src/python/grpcio/grpc/framework/alpha/_reexport.py b/src/python/grpcio/grpc/framework/alpha/_reexport.py new file mode 100644 index 0000000000..198cb95ad5 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/_reexport.py @@ -0,0 +1,203 @@ +# 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. + +from grpc.framework.common import cardinality +from grpc.framework.face import exceptions as face_exceptions +from grpc.framework.face import interfaces as face_interfaces +from grpc.framework.foundation import future +from grpc.framework.alpha import exceptions +from grpc.framework.alpha import interfaces + +_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = { + interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, + interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, + interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, + interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, +} + +_ABORTION_REEXPORT = { + face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED, + face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED, + face_interfaces.Abortion.NETWORK_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + face_interfaces.Abortion.SERVICED_FAILURE: + interfaces.Abortion.SERVICED_FAILURE, + face_interfaces.Abortion.SERVICER_FAILURE: + interfaces.Abortion.SERVICER_FAILURE, +} + + +class _RpcError(exceptions.RpcError): + pass + + +def _reexport_error(face_rpc_error): + if isinstance(face_rpc_error, face_exceptions.CancellationError): + return exceptions.CancellationError() + elif isinstance(face_rpc_error, face_exceptions.ExpirationError): + return exceptions.ExpirationError() + else: + return _RpcError() + + +def _as_face_abortion_callback(abortion_callback): + def face_abortion_callback(face_abortion): + abortion_callback(_ABORTION_REEXPORT[face_abortion]) + return face_abortion_callback + + +class _ReexportedFuture(future.Future): + + def __init__(self, face_future): + self._face_future = face_future + + def cancel(self): + return self._face_future.cancel() + + def cancelled(self): + return self._face_future.cancelled() + + def running(self): + return self._face_future.running() + + def done(self): + return self._face_future.done() + + def result(self, timeout=None): + try: + return self._face_future.result(timeout=timeout) + except face_exceptions.RpcError as e: + raise _reexport_error(e) + + def exception(self, timeout=None): + face_error = self._face_future.exception(timeout=timeout) + return None if face_error is None else _reexport_error(face_error) + + def traceback(self, timeout=None): + return self._face_future.traceback(timeout=timeout) + + def add_done_callback(self, fn): + self._face_future.add_done_callback(lambda unused_face_future: fn(self)) + + +def _call_reexporting_errors(behavior, *args, **kwargs): + try: + return behavior(*args, **kwargs) + except face_exceptions.RpcError as e: + raise _reexport_error(e) + + +def _reexported_future(face_future): + return _ReexportedFuture(face_future) + + +class _CancellableIterator(interfaces.CancellableIterator): + + def __init__(self, face_cancellable_iterator): + self._face_cancellable_iterator = face_cancellable_iterator + + def __iter__(self): + return self + + def next(self): + return _call_reexporting_errors(self._face_cancellable_iterator.next) + + def cancel(self): + self._face_cancellable_iterator.cancel() + + +class _RpcContext(interfaces.RpcContext): + + def __init__(self, face_rpc_context): + self._face_rpc_context = face_rpc_context + + def is_active(self): + return self._face_rpc_context.is_active() + + def time_remaining(self): + return self._face_rpc_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + self._face_rpc_context.add_abortion_callback( + _as_face_abortion_callback(abortion_callback)) + + +class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync): + + def __init__(self, face_unary_unary_multi_callable): + self._underlying = face_unary_unary_multi_callable + + def __call__(self, request, timeout): + return _call_reexporting_errors( + self._underlying, request, timeout) + + def async(self, request, timeout): + return _ReexportedFuture(self._underlying.future(request, timeout)) + + +class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync): + + def __init__(self, face_stream_unary_multi_callable): + self._underlying = face_stream_unary_multi_callable + + def __call__(self, request_iterator, timeout): + return _call_reexporting_errors( + self._underlying, request_iterator, timeout) + + def async(self, request_iterator, timeout): + return _ReexportedFuture(self._underlying.future(request_iterator, timeout)) + + +def common_cardinality(early_adopter_cardinality): + return _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ + early_adopter_cardinality] + + +def common_cardinalities(early_adopter_cardinalities): + common_cardinalities = {} + for name, early_adopter_cardinality in early_adopter_cardinalities.iteritems(): + common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ + early_adopter_cardinality] + return common_cardinalities + + +def rpc_context(face_rpc_context): + return _RpcContext(face_rpc_context) + + +def cancellable_iterator(face_cancellable_iterator): + return _CancellableIterator(face_cancellable_iterator) + + +def unary_unary_sync_async(face_unary_unary_multi_callable): + return _UnaryUnarySyncAsync(face_unary_unary_multi_callable) + + +def stream_unary_sync_async(face_stream_unary_multi_callable): + return _StreamUnarySyncAsync(face_stream_unary_multi_callable) diff --git a/src/python/grpcio/grpc/framework/alpha/exceptions.py b/src/python/grpcio/grpc/framework/alpha/exceptions.py new file mode 100644 index 0000000000..5234d3b91c --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/exceptions.py @@ -0,0 +1,48 @@ +# 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. + +"""Exceptions raised by GRPC. + +Only GRPC should instantiate and raise these exceptions. +""" + +import abc + + +class RpcError(Exception): + """Common super type for all exceptions raised by GRPC.""" + __metaclass__ = abc.ABCMeta + + +class CancellationError(RpcError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(RpcError): + """Indicates that an RPC has expired ("timed out").""" diff --git a/src/python/grpcio/grpc/framework/alpha/interfaces.py b/src/python/grpcio/grpc/framework/alpha/interfaces.py new file mode 100644 index 0000000000..8380567c97 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/interfaces.py @@ -0,0 +1,388 @@ +# 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. + +"""Interfaces of GRPC.""" + +import abc +import enum + +# exceptions is referenced from specification in this module. +from grpc.framework.alpha import exceptions # pylint: disable=unused-import +from grpc.framework.foundation import activated +from grpc.framework.foundation import future + + +@enum.unique +class Cardinality(enum.Enum): + """Constants for the four cardinalities of RPC.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' + + +@enum.unique +class Abortion(enum.Enum): + """Categories of RPC abortion.""" + + CANCELLED = 'cancelled' + EXPIRED = 'expired' + NETWORK_FAILURE = 'network failure' + SERVICED_FAILURE = 'serviced failure' + SERVICER_FAILURE = 'servicer failure' + + +class CancellableIterator(object): + """Implements the Iterator protocol and affords a cancel method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __iter__(self): + """Returns the self object in accordance with the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def next(self): + """Returns a value or raises StopIteration per the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of whatever computation underlies this iterator.""" + raise NotImplementedError() + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + Args: + abortion_callback: A callable to be called and passed an Abortion value + in the event of RPC abortion. + """ + raise NotImplementedError() + + +class UnaryUnarySyncAsync(object): + """Affords invoking a unary-unary RPC synchronously or asynchronously. + Values implementing this interface are directly callable and present an + "async" method. Both calls take a request value and a numeric timeout. + Direct invocation of a value of this type invokes its associated RPC and + blocks until the RPC's response is available. Calling the "async" method + of a value of this type invokes its associated RPC and immediately returns a + future.Future bound to the asynchronous execution of the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + Returns: + The response value for the RPC. + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def async(self, request, timeout): + """Asynchronously invokes the underlying RPC. + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + +class StreamUnarySyncAsync(object): + """Affords invoking a stream-unary RPC synchronously or asynchronously. + Values implementing this interface are directly callable and present an + "async" method. Both calls take an iterator of request values and a numeric + timeout. Direct invocation of a value of this type invokes its associated RPC + and blocks until the RPC's response is available. Calling the "async" method + of a value of this type invokes its associated RPC and immediately returns a + future.Future bound to the asynchronous execution of the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def async(self, request_iterator, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + +class RpcMethodDescription(object): + """A type for the common aspects of RPC method descriptions.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cardinality(self): + """Identifies the cardinality of this RpcMethodDescription. + + Returns: + A Cardinality value identifying whether or not this + RpcMethodDescription is request-unary or request-streaming and + whether or not it is response-unary or response-streaming. + """ + raise NotImplementedError() + + +class RpcMethodInvocationDescription(RpcMethodDescription): + """Invocation-side description of an RPC method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def serialize_request(self, request): + """Serializes a request value. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodInvocationDescription. + + Returns: + The serialization of the given request value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Deserializes a response value. + + Args: + serialized_response: A bytestring that is the serialization of a response + value appropriate for the RPC method described by this + RpcMethodInvocationDescription. + + Returns: + A response value corresponding to the given bytestring. + """ + raise NotImplementedError() + + +class RpcMethodServiceDescription(RpcMethodDescription): + """Service-side description of an RPC method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Deserializes a request value. + + Args: + serialized_request: A bytestring that is the serialization of a request + value appropriate for the RPC method described by this + RpcMethodServiceDescription. + + Returns: + A request value corresponding to the given bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serializes a response value. + + Args: + response: A response value appropriate for the RPC method described by + this RpcMethodServiceDescription. + + Returns: + The serialization of the given response value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_unary(self, request, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.UNARY_UNARY. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Returns: + A response value appropriate for the RPC method described by this + RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_stream(self, request, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.UNARY_STREAM. + + Args: + request: A request value appropriate for the RPC method described by this + RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Yields: + Zero or more response values appropriate for the RPC method described by + this RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_unary(self, request_iterator, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.STREAM_UNARY. + + Args: + request_iterator: An iterator of request values appropriate for the RPC + method described by this RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Returns: + A response value appropriate for the RPC method described by this + RpcMethodServiceDescription. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_stream(self, request_iterator, context): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethodServiceDescription is Cardinality.STREAM_STREAM. + + Args: + request_iterator: An iterator of request values appropriate for the RPC + method described by this RpcMethodServiceDescription. + context: An RpcContext object for the RPC. + + Yields: + Zero or more response values appropriate for the RPC method described by + this RpcMethodServiceDescription. + """ + raise NotImplementedError() + + +class Stub(object): + """A stub with callable RPC method names for attributes. + + Instances of this type are context managers and only afford RPC invocation + when used in context. + + Instances of this type, when used in context, respond to attribute access + as follows: if the requested attribute is the name of a unary-unary RPC + method, the value of the attribute will be a UnaryUnarySyncAsync with which + to invoke the RPC method. If the requested attribute is the name of a + unary-stream RPC method, the value of the attribute will be a callable taking + a request object and a timeout parameter and returning a CancellableIterator + that yields the response values of the RPC. If the requested attribute is the + name of a stream-unary RPC method, the value of the attribute will be a + StreamUnarySyncAsync with which to invoke the RPC method. If the requested + attribute is the name of a stream-stream RPC method, the value of the + attribute will be a callable taking an iterator of request objects and a + timeout and returning a CancellableIterator that yields the response values + of the RPC. + + In all cases indication of abortion is indicated by raising of + exceptions.RpcError, exceptions.CancellationError, + and exceptions.ExpirationError. + """ + __metaclass__ = abc.ABCMeta + + +class Server(activated.Activated): + """A GRPC Server.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def port(self): + """Reports the port on which the server is serving. + + This method may only be called while the server is activated. + + Returns: + The port on which the server is serving. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/alpha/utilities.py b/src/python/grpcio/grpc/framework/alpha/utilities.py new file mode 100644 index 0000000000..7d7f78f5e4 --- /dev/null +++ b/src/python/grpcio/grpc/framework/alpha/utilities.py @@ -0,0 +1,269 @@ +# 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. + +"""Utilities for use with GRPC.""" + +from grpc.framework.alpha import interfaces + + +class _RpcMethodDescription( + interfaces.RpcMethodInvocationDescription, + interfaces.RpcMethodServiceDescription): + + def __init__( + self, cardinality, unary_unary, unary_stream, stream_unary, + stream_stream, request_serializer, request_deserializer, + response_serializer, response_deserializer): + self._cardinality = cardinality + self._unary_unary = unary_unary + self._unary_stream = unary_stream + self._stream_unary = stream_unary + self._stream_stream = stream_stream + self._request_serializer = request_serializer + self._request_deserializer = request_deserializer + self._response_serializer = response_serializer + self._response_deserializer = response_deserializer + + def cardinality(self): + """See interfaces.RpcMethodDescription.cardinality for specification.""" + return self._cardinality + + def serialize_request(self, request): + """See interfaces.RpcMethodInvocationDescription.serialize_request.""" + return self._request_serializer(request) + + def deserialize_request(self, serialized_request): + """See interfaces.RpcMethodServiceDescription.deserialize_request.""" + return self._request_deserializer(serialized_request) + + def serialize_response(self, response): + """See interfaces.RpcMethodServiceDescription.serialize_response.""" + return self._response_serializer(response) + + def deserialize_response(self, serialized_response): + """See interfaces.RpcMethodInvocationDescription.deserialize_response.""" + return self._response_deserializer(serialized_response) + + def service_unary_unary(self, request, context): + """See interfaces.RpcMethodServiceDescription.service_unary_unary.""" + return self._unary_unary(request, context) + + def service_unary_stream(self, request, context): + """See interfaces.RpcMethodServiceDescription.service_unary_stream.""" + return self._unary_stream(request, context) + + def service_stream_unary(self, request_iterator, context): + """See interfaces.RpcMethodServiceDescription.service_stream_unary.""" + return self._stream_unary(request_iterator, context) + + def service_stream_stream(self, request_iterator, context): + """See interfaces.RpcMethodServiceDescription.service_stream_stream.""" + return self._stream_stream(request_iterator, context) + + +def unary_unary_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a unary-request/unary-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_UNARY, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def unary_stream_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a unary-request/streaming-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_STREAM, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def stream_unary_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a streaming-request/unary-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_UNARY, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def stream_stream_invocation_description( + request_serializer, response_deserializer): + """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. + + Args: + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethodInvocationDescription constructed from the given + arguments representing a streaming-request/streaming-response RPC + method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_STREAM, None, None, None, None, + request_serializer, None, None, response_deserializer) + + +def unary_unary_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a unary-unary RPC + method that accepts a single request and an interfaces.RpcContext and + returns a single response. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a unary-request/unary-response RPC + method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None, + None, request_deserializer, response_serializer, None) + + +def unary_stream_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a unary-stream RPC + method that accepts a single request and an interfaces.RpcContext + and returns an iterator of zero or more responses. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a unary-request/streaming-response + RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None, + None, request_deserializer, response_serializer, None) + + +def stream_unary_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a stream-unary RPC + method that accepts an iterator of zero or more requests + and an interfaces.RpcContext and returns a single response. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a streaming-request/unary-response + RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None, + None, request_deserializer, response_serializer, None) + + +def stream_stream_service_description( + behavior, request_deserializer, response_serializer): + """Creates an interfaces.RpcMethodServiceDescription for the given behavior. + + Args: + behavior: A callable that implements a stream-stream RPC + method that accepts an iterator of zero or more requests + and an interfaces.RpcContext and returns an iterator of + zero or more responses. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + + Returns: + An interfaces.RpcMethodServiceDescription constructed from the given + arguments representing a + streaming-request/streaming-response RPC method. + """ + return _RpcMethodDescription( + interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior, + None, request_deserializer, response_serializer, None) diff --git a/src/python/grpcio/grpc/framework/base/__init__.py b/src/python/grpcio/grpc/framework/base/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/base/_cancellation.py b/src/python/grpcio/grpc/framework/base/_cancellation.py new file mode 100644 index 0000000000..ffbc90668f --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_cancellation.py @@ -0,0 +1,64 @@ +# 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. + +"""State and behavior for operation cancellation.""" + +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces + + +class CancellationManager(_interfaces.CancellationManager): + """An implementation of _interfaces.CancellationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def cancel(self): + """See _interfaces.CancellationManager.cancel for specification.""" + with self._lock: + self._termination_manager.abort(interfaces.Outcome.CANCELLED) + self._transmission_manager.abort(interfaces.Outcome.CANCELLED) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/grpcio/grpc/framework/base/_constants.py b/src/python/grpcio/grpc/framework/base/_constants.py new file mode 100644 index 0000000000..8fbdc82782 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_constants.py @@ -0,0 +1,32 @@ +# 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. + +"""Private constants for the package.""" + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Base) internal error! :-(' diff --git a/src/python/grpcio/grpc/framework/base/_context.py b/src/python/grpcio/grpc/framework/base/_context.py new file mode 100644 index 0000000000..d84871d639 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_context.py @@ -0,0 +1,99 @@ +# 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. + +"""State and behavior for operation context.""" + +import time + +# _interfaces is referenced from specification in this module. +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces # pylint: disable=unused-import + + +class OperationContext(interfaces.OperationContext): + """An implementation of interfaces.OperationContext.""" + + def __init__( + self, lock, operation_id, local_failure, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + operation_id: An object identifying the operation. + local_failure: Whichever one of interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes local failure of + customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._local_failure = local_failure + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self.operation_id = operation_id + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets managers with which this OperationContext cooperates. + + Args: + ingestion_manager: The _interfaces.IngestionManager for the operation. + expiration_manager: The _interfaces.ExpirationManager for the operation. + """ + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def is_active(self): + """See interfaces.OperationContext.is_active for specification.""" + with self._lock: + return self._termination_manager.is_active() + + def add_termination_callback(self, callback): + """See interfaces.OperationContext.add_termination_callback.""" + with self._lock: + self._termination_manager.add_callback(callback) + + def time_remaining(self): + """See interfaces.OperationContext.time_remaining for specification.""" + with self._lock: + deadline = self._expiration_manager.deadline() + return max(0.0, deadline - time.time()) + + def fail(self, exception): + """See interfaces.OperationContext.fail for specification.""" + with self._lock: + self._termination_manager.abort(self._local_failure) + self._transmission_manager.abort(self._local_failure) + self._ingestion_manager.abort() + self._expiration_manager.abort() diff --git a/src/python/grpcio/grpc/framework/base/_emission.py b/src/python/grpcio/grpc/framework/base/_emission.py new file mode 100644 index 0000000000..1829669a72 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_emission.py @@ -0,0 +1,125 @@ +# 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. + +"""State and behavior for handling emitted values.""" + +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces + + +class _EmissionManager(_interfaces.EmissionManager): + """An implementation of _interfaces.EmissionManager.""" + + def __init__( + self, lock, failure_outcome, termination_manager, transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + failure_outcome: Whichever one of interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes this object's methods + being called inappropriately by customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._failure_outcome = failure_outcome + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emission_complete = False + + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _abort(self): + self._termination_manager.abort(self._failure_outcome) + self._transmission_manager.abort(self._failure_outcome) + self._ingestion_manager.abort() + self._expiration_manager.abort() + + def consume(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._transmission_manager.inmit(value, False) + + def terminate(self): + with self._lock: + if not self._emission_complete: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(None, True) + self._emission_complete = True + + def consume_and_terminate(self, value): + with self._lock: + if self._emission_complete: + self._abort() + else: + self._termination_manager.emission_complete() + self._transmission_manager.inmit(value, True) + self._emission_complete = True + + +def front_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for front-side use. + """ + return _EmissionManager( + lock, interfaces.Outcome.SERVICED_FAILURE, termination_manager, + transmission_manager) + + +def back_emission_manager(lock, termination_manager, transmission_manager): + """Creates an _interfaces.EmissionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the operation. + + Returns: + An _interfaces.EmissionManager appropriate for back-side use. + """ + return _EmissionManager( + lock, interfaces.Outcome.SERVICER_FAILURE, termination_manager, + transmission_manager) diff --git a/src/python/grpcio/grpc/framework/base/_ends.py b/src/python/grpcio/grpc/framework/base/_ends.py new file mode 100644 index 0000000000..176f3ac06e --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_ends.py @@ -0,0 +1,399 @@ +# 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. + +"""Implementations of FrontLinks and BackLinks.""" + +import collections +import threading +import uuid + +# _interfaces is referenced from specification in this module. +from grpc.framework.base import _cancellation +from grpc.framework.base import _context +from grpc.framework.base import _emission +from grpc.framework.base import _expiration +from grpc.framework.base import _ingestion +from grpc.framework.base import _interfaces # pylint: disable=unused-import +from grpc.framework.base import _reception +from grpc.framework.base import _termination +from grpc.framework.base import _transmission +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' + + +class _EasyOperation(interfaces.Operation): + """A trivial implementation of interfaces.Operation.""" + + def __init__(self, emission_manager, context, cancellation_manager): + """Constructor. + + Args: + emission_manager: The _interfaces.EmissionManager for the operation that + will accept values emitted by customer code. + context: The interfaces.OperationContext for use by the customer + during the operation. + cancellation_manager: The _interfaces.CancellationManager for the + operation. + """ + self.consumer = emission_manager + self.context = context + self._cancellation_manager = cancellation_manager + + def cancel(self): + self._cancellation_manager.cancel() + + +class _Endlette(object): + """Utility for stateful behavior common to Fronts and Backs.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to use when calling registered idle actions. + """ + self._lock = threading.Lock() + self._pool = pool + # Dictionary from operation IDs to ReceptionManager-or-None. A None value + # indicates an in-progress fire-and-forget operation for which the customer + # has chosen to ignore results. + self._operations = {} + self._stats = {outcome: 0 for outcome in interfaces.Outcome} + self._idle_actions = [] + + def terminal_action(self, operation_id): + """Constructs the termination action for a single operation. + + Args: + operation_id: An operation ID. + + Returns: + A callable that takes an operation outcome for an argument to be used as + the termination action for the operation associated with the given + operation ID. + """ + def termination_action(outcome): + with self._lock: + self._stats[outcome] += 1 + self._operations.pop(operation_id, None) + if not self._operations: + for action in self._idle_actions: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + self._idle_actions = [] + return termination_action + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._lock.release() + + def get_operation(self, operation_id): + return self._operations.get(operation_id, None) + + def add_operation(self, operation_id, operation_reception_manager): + self._operations[operation_id] = operation_reception_manager + + def operation_stats(self): + with self._lock: + return dict(self._stats) + + def add_idle_action(self, action): + with self._lock: + if self._operations: + self._idle_actions.append(action) + else: + self._pool.submit(callable_util.with_exceptions_logged( + action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) + + +class _FrontManagement( + collections.namedtuple( + '_FrontManagement', + ('reception', 'emission', 'operation', 'cancellation'))): + """Just a trivial helper class to bundle four fellow-traveling objects.""" + + +def _front_operate( + callback, work_pool, transmission_pool, utility_pool, + termination_action, operation_id, name, payload, complete, timeout, + subscription, trace_id): + """Constructs objects necessary for front-side operation management. + + Args: + callback: A callable that accepts interfaces.FrontToBackTickets and + delivers them to the other side of the operation. Execution of this + callable may take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + operation_id: An object identifying the operation. + name: The name of the method being called during the operation. + payload: The first customer-significant value to be transmitted to the other + side. May be None if there is no such value or if the customer chose not + to pass it at operation invocation. + complete: A boolean indicating whether or not additional payloads will be + supplied by the customer. + timeout: A length of time in seconds to allow for the operation. + subscription: A interfaces.ServicedSubscription describing the + customer's interest in the results of the operation. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + + Returns: + A _FrontManagement object bundling together the + _interfaces.ReceptionManager, _interfaces.EmissionManager, + _context.OperationContext, and _interfaces.CancellationManager for the + operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.front_termination_manager( + work_pool, utility_pool, termination_action, subscription.kind) + transmission_manager = _transmission.front_transmission_manager( + lock, transmission_pool, callback, operation_id, name, + subscription.kind, trace_id, timeout, termination_manager) + operation_context = _context.OperationContext( + lock, operation_id, interfaces.Outcome.SERVICED_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.front_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.front_ingestion_manager( + lock, work_pool, subscription, termination_manager, + transmission_manager, operation_context) + expiration_manager = _expiration.front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout) + reception_manager = _reception.front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + cancellation_manager = _cancellation.CancellationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + transmission_manager.inmit(payload, complete) + + if subscription.kind is interfaces.ServicedSubscription.Kind.NONE: + returned_reception_manager = None + else: + returned_reception_manager = reception_manager + + return _FrontManagement( + returned_reception_manager, emission_manager, operation_context, + cancellation_manager) + + +class FrontLink(interfaces.FrontLink): + """An implementation of interfaces.FrontLink.""" + + def __init__(self, work_pool, transmission_pool, utility_pool): + """Constructor. + + Args: + work_pool: A thread pool to be used for executing customer code. + transmission_pool: A thread pool to be used for transmitting values to + the other side of the operation. + utility_pool: A thread pool to be used for utility tasks. + """ + self._endlette = _Endlette(utility_pool) + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._callback = None + + self._operations = {} + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + with self._endlette: + self._callback = rear_link.accept_front_to_back_ticket + + def operation_stats(self): + """See interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) + + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """See interfaces.Front.operate for specification.""" + operation_id = uuid.uuid4() + with self._endlette: + management = _front_operate( + self._callback, self._work_pool, self._transmission_pool, + self._utility_pool, self._endlette.terminal_action(operation_id), + operation_id, name, payload, complete, timeout, subscription, + trace_id) + self._endlette.add_operation(operation_id, management.reception) + return _EasyOperation( + management.emission, management.operation, management.cancellation) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.End.act for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager: + reception_manager.receive_ticket(ticket) + + +def _back_operate( + servicer, callback, work_pool, transmission_pool, utility_pool, + termination_action, ticket, default_timeout, maximum_timeout): + """Constructs objects necessary for back-side operation management. + + Also begins back-side operation by feeding the first received ticket into the + constructed _interfaces.ReceptionManager. + + Args: + servicer: An interfaces.Servicer for servicing operations. + callback: A callable that accepts interfaces.BackToFrontTickets and + delivers them to the other side of the operation. Execution of this + callable may take any arbitrary length of time. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + termination_action: A no-arg behavior to be called upon operation + completion. + ticket: The first interfaces.FrontToBackTicket received for the operation. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + The _interfaces.ReceptionManager to be used for the operation. + """ + lock = threading.Lock() + with lock: + termination_manager = _termination.back_termination_manager( + work_pool, utility_pool, termination_action, ticket.subscription) + transmission_manager = _transmission.back_transmission_manager( + lock, transmission_pool, callback, ticket.operation_id, + termination_manager, ticket.subscription) + operation_context = _context.OperationContext( + lock, ticket.operation_id, interfaces.Outcome.SERVICER_FAILURE, + termination_manager, transmission_manager) + emission_manager = _emission.back_emission_manager( + lock, termination_manager, transmission_manager) + ingestion_manager = _ingestion.back_ingestion_manager( + lock, work_pool, servicer, termination_manager, + transmission_manager, operation_context, emission_manager) + expiration_manager = _expiration.back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + ticket.timeout, default_timeout, maximum_timeout) + reception_manager = _reception.back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager) + + termination_manager.set_expiration_manager(expiration_manager) + transmission_manager.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + operation_context.set_ingestion_and_expiration_managers( + ingestion_manager, expiration_manager) + emission_manager.set_ingestion_manager_and_expiration_manager( + ingestion_manager, expiration_manager) + ingestion_manager.set_expiration_manager(expiration_manager) + + reception_manager.receive_ticket(ticket) + + return reception_manager + + +class BackLink(interfaces.BackLink): + """An implementation of interfaces.BackLink.""" + + def __init__( + self, servicer, work_pool, transmission_pool, utility_pool, + default_timeout, maximum_timeout): + """Constructor. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool in which to execute customer code. + transmission_pool: A thread pool to use for transmitting to the other side + of the operation. + utility_pool: A thread pool for utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + """ + self._endlette = _Endlette(utility_pool) + self._servicer = servicer + self._work_pool = work_pool + self._transmission_pool = transmission_pool + self._utility_pool = utility_pool + self._default_timeout = default_timeout + self._maximum_timeout = maximum_timeout + self._callback = None + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + with self._endlette: + self._callback = fore_link.accept_back_to_front_ticket + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.RearLink.accept_front_to_back_ticket for specification.""" + with self._endlette: + reception_manager = self._endlette.get_operation(ticket.operation_id) + if reception_manager is None: + reception_manager = _back_operate( + self._servicer, self._callback, self._work_pool, + self._transmission_pool, self._utility_pool, + self._endlette.terminal_action(ticket.operation_id), ticket, + self._default_timeout, self._maximum_timeout) + self._endlette.add_operation(ticket.operation_id, reception_manager) + else: + reception_manager.receive_ticket(ticket) + + def operation_stats(self): + """See interfaces.End.operation_stats for specification.""" + return self._endlette.operation_stats() + + def add_idle_action(self, action): + """See interfaces.End.add_idle_action for specification.""" + self._endlette.add_idle_action(action) diff --git a/src/python/grpcio/grpc/framework/base/_expiration.py b/src/python/grpcio/grpc/framework/base/_expiration.py new file mode 100644 index 0000000000..17acbef4c1 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_expiration.py @@ -0,0 +1,158 @@ +# 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. + +"""State and behavior for operation expiration.""" + +import time + +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import later + + +class _ExpirationManager(_interfaces.ExpirationManager): + """An implementation of _interfaces.ExpirationManager.""" + + def __init__( + self, lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, maximum_timeout): + """Constructor. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + commencement: The time in seconds since the epoch at which the operation + began. + timeout: A length of time in seconds to allow for the operation to run. + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run despite what is requested via this object's + change_timout method. + """ + self._lock = lock + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._commencement = commencement + self._maximum_timeout = maximum_timeout + + self._timeout = timeout + self._deadline = commencement + timeout + self._index = None + self._future = None + + def _expire(self, index): + with self._lock: + if self._future is not None and index == self._index: + self._future = None + self._termination_manager.abort(interfaces.Outcome.EXPIRED) + self._transmission_manager.abort(interfaces.Outcome.EXPIRED) + self._ingestion_manager.abort() + + def start(self): + self._index = 0 + self._future = later.later(self._timeout, lambda: self._expire(0)) + + def change_timeout(self, timeout): + if self._future is not None and timeout != self._timeout: + self._future.cancel() + new_timeout = min(timeout, self._maximum_timeout) + new_index = self._index + 1 + self._timeout = new_timeout + self._deadline = self._commencement + new_timeout + self._index = new_index + delay = self._deadline - time.time() + self._future = later.later( + delay, lambda: self._expire(new_index)) + + def deadline(self): + return self._deadline + + def abort(self): + if self._future: + self._future.cancel() + self._future = None + self._deadline_index = None + + +def front_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout): + """Creates an _interfaces.ExpirationManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for front-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, timeout, timeout) + expiration_manager.start() + return expiration_manager + + +def back_expiration_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + timeout, default_timeout, maximum_timeout): + """Creates an _interfaces.ExpirationManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + ingestion_manager: The _interfaces.IngestionManager for the operation. + timeout: A length of time in seconds to allow for the operation to run. May + be None in which case default_timeout will be used. + default_timeout: The default length of time in seconds to allow for the + operation to run if the front-side customer has not specified such a value + (or if the value they specified is not yet known). + maximum_timeout: The maximum length of time in seconds to allow for the + operation to run. + + Returns: + An _interfaces.ExpirationManager appropriate for back-side use. + """ + commencement = time.time() + expiration_manager = _ExpirationManager( + lock, termination_manager, transmission_manager, ingestion_manager, + commencement, default_timeout if timeout is None else timeout, + maximum_timeout) + expiration_manager.start() + return expiration_manager diff --git a/src/python/grpcio/grpc/framework/base/_ingestion.py b/src/python/grpcio/grpc/framework/base/_ingestion.py new file mode 100644 index 0000000000..06d5b92f0b --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_ingestion.py @@ -0,0 +1,442 @@ +# 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. + +"""State and behavior for ingestion during an operation.""" + +import abc +import collections + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import exceptions +from grpc.framework.base import interfaces +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import stream + +_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' +_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' + + +class _ConsumerCreation(collections.namedtuple( + '_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))): + """A sum type for the outcome of ingestion initialization. + + Either consumer will be non-None, remote_error will be True, or abandoned will + be True. + + Attributes: + consumer: A stream.Consumer for ingesting payloads. + remote_error: A boolean indicating that the consumer could not be created + due to an error on the remote side of the operation. + abandoned: A boolean indicating that the consumer creation was abandoned. + """ + + +class _EmptyConsumer(stream.Consumer): + """A no-operative stream.Consumer that ignores all inputs and calls.""" + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + + +class _ConsumerCreator(object): + """Common specification of different consumer-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def create_consumer(self, requirement): + """Creates the stream.Consumer to which customer payloads will be delivered. + + Any exceptions raised by this method should be attributed to and treated as + defects in the serviced or servicer code called by this method. + + Args: + requirement: A value required by this _ConsumerCreator for consumer + creation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + raise NotImplementedError() + + +class _FrontConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for front-side use.""" + + def __init__(self, subscription, operation_context): + """Constructor. + + Args: + subscription: The serviced's interfaces.ServicedSubscription for the + operation. + operation_context: The interfaces.OperationContext object for the + operation. + """ + self._subscription = subscription + self._operation_context = operation_context + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for specification.""" + if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL: + try: + return _ConsumerCreation( + self._subscription.ingestor.consumer(self._operation_context), + False, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + else: + return _ConsumerCreation(_EmptyConsumer(), False, False) + + +class _BackConsumerCreator(_ConsumerCreator): + """A _ConsumerCreator appropriate for back-side use.""" + + def __init__(self, servicer, operation_context, emission_consumer): + """Constructor. + + Args: + servicer: The interfaces.Servicer that will service the operation. + operation_context: The interfaces.OperationContext object for the + operation. + emission_consumer: The stream.Consumer object to which payloads emitted + from the operation will be passed. + """ + self._servicer = servicer + self._operation_context = operation_context + self._emission_consumer = emission_consumer + + def create_consumer(self, requirement): + """See _ConsumerCreator.create_consumer for full specification. + + Args: + requirement: The name of the Servicer method to be called during this + operation. + + Returns: + A _ConsumerCreation describing the result of consumer creation. + """ + try: + return _ConsumerCreation( + self._servicer.service( + requirement, self._operation_context, self._emission_consumer), + False, False) + except exceptions.NoSuchMethodError: + return _ConsumerCreation(None, True, False) + except abandonment.Abandoned: + return _ConsumerCreation(None, False, True) + + +class _WrappedConsumer(object): + """Wraps a consumer to catch the exceptions that it is allowed to throw.""" + + def __init__(self, consumer): + """Constructor. + + Args: + consumer: A stream.Consumer that may raise abandonment.Abandoned from any + of its methods. + """ + self._consumer = consumer + + def moar(self, payload, complete): + """Makes progress with the wrapped consumer. + + This method catches all exceptions allowed to be thrown by the wrapped + consumer. Any exceptions raised by this method should be blamed on the + customer-supplied consumer. + + Args: + payload: A customer-significant payload object. May be None only if + complete is True. + complete: Whether or not the end of the payload sequence has been reached. + Must be True if payload is None. + + Returns: + True if the wrapped consumer made progress or False if the wrapped + consumer raised abandonment.Abandoned to indicate its abandonment of + progress. + """ + try: + if payload is None: + self._consumer.terminate() + elif complete: + self._consumer.consume_and_terminate(payload) + else: + self._consumer.consume(payload) + return True + except abandonment.Abandoned: + return False + + +class _IngestionManager(_interfaces.IngestionManager): + """An implementation of _interfaces.IngestionManager.""" + + def __init__( + self, lock, pool, consumer_creator, failure_outcome, termination_manager, + transmission_manager): + """Constructor. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + consumer_creator: A _ConsumerCreator wrapping the portion of customer code + that when called returns the stream.Consumer with which the customer + code will ingest payload values. + failure_outcome: Whichever one of + interfaces.Outcome.SERVICED_FAILURE or + interfaces.Outcome.SERVICER_FAILURE describes local failure of + customer code. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + """ + self._lock = lock + self._pool = pool + self._consumer_creator = consumer_creator + self._failure_outcome = failure_outcome + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._expiration_manager = None + + self._wrapped_ingestion_consumer = None + self._pending_ingestion = [] + self._ingestion_complete = False + self._processing = False + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _abort_internal_only(self): + self._wrapped_ingestion_consumer = None + self._pending_ingestion = None + + def _abort_and_notify(self, outcome): + self._abort_internal_only() + self._termination_manager.abort(outcome) + self._transmission_manager.abort(outcome) + self._expiration_manager.abort() + + def _next(self): + """Computes the next step for ingestion. + + Returns: + A payload, complete, continue triplet indicating what payload (if any) is + available to feed into customer code, whether or not the sequence of + payloads has terminated, and whether or not there is anything + immediately actionable to call customer code to do. + """ + if self._pending_ingestion is None: + return None, False, False + elif self._pending_ingestion: + payload = self._pending_ingestion.pop(0) + complete = self._ingestion_complete and not self._pending_ingestion + return payload, complete, True + elif self._ingestion_complete: + return None, True, True + else: + return None, False, False + + def _process(self, wrapped_ingestion_consumer, payload, complete): + """A method to call to execute customer code. + + This object's lock must *not* be held when calling this method. + + Args: + wrapped_ingestion_consumer: The _WrappedConsumer with which to pass + payloads to customer code. + payload: A customer payload. May be None only if complete is True. + complete: Whether or not the sequence of payloads to pass to the customer + has concluded. + """ + while True: + consumption_outcome = callable_util.call_logging_exceptions( + wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE, + payload, complete) + if consumption_outcome.exception is None: + if consumption_outcome.return_value: + with self._lock: + if complete: + self._pending_ingestion = None + self._termination_manager.ingestion_complete() + return + else: + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + else: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_outcome) + self._processing = False + return + else: + with self._lock: + self._abort_and_notify(self._failure_outcome) + self._processing = False + return + + def start(self, requirement): + if self._pending_ingestion is not None: + def initialize(): + consumer_creation_outcome = callable_util.call_logging_exceptions( + self._consumer_creator.create_consumer, + _CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement) + if consumer_creation_outcome.return_value is None: + with self._lock: + self._abort_and_notify(self._failure_outcome) + self._processing = False + elif consumer_creation_outcome.return_value.remote_error: + with self._lock: + self._abort_and_notify(interfaces.Outcome.RECEPTION_FAILURE) + self._processing = False + elif consumer_creation_outcome.return_value.abandoned: + with self._lock: + if self._pending_ingestion is not None: + self._abort_and_notify(self._failure_outcome) + self._processing = False + else: + wrapped_ingestion_consumer = _WrappedConsumer( + consumer_creation_outcome.return_value.consumer) + with self._lock: + self._wrapped_ingestion_consumer = wrapped_ingestion_consumer + payload, complete, moar = self._next() + if not moar: + self._processing = False + return + + self._process(wrapped_ingestion_consumer, payload, complete) + + self._pool.submit( + callable_util.with_exceptions_logged( + initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE)) + self._processing = True + + def consume(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + elif self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, False) + self._processing = True + + def terminate(self): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None and not self._processing: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, None, True) + self._processing = True + + def consume_and_terminate(self, payload): + if self._ingestion_complete: + self._abort_and_notify(self._failure_outcome) + else: + self._ingestion_complete = True + if self._pending_ingestion is not None: + if self._processing: + self._pending_ingestion.append(payload) + else: + self._pool.submit( + callable_util.with_exceptions_logged( + self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._wrapped_ingestion_consumer, payload, True) + self._processing = True + + def abort(self): + """See _interfaces.IngestionManager.abort for specification.""" + self._abort_internal_only() + + +def front_ingestion_manager( + lock, pool, subscription, termination_manager, transmission_manager, + operation_context): + """Creates an IngestionManager appropriate for front-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + subscription: A interfaces.ServicedSubscription indicating the + customer's interest in the results of the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A interfaces.OperationContext for the operation. + + Returns: + An IngestionManager appropriate for front-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _FrontConsumerCreator(subscription, operation_context), + interfaces.Outcome.SERVICED_FAILURE, termination_manager, + transmission_manager) + ingestion_manager.start(None) + return ingestion_manager + + +def back_ingestion_manager( + lock, pool, servicer, termination_manager, transmission_manager, + operation_context, emission_consumer): + """Creates an IngestionManager appropriate for back-side use. + + Args: + lock: The operation-wide lock. + pool: A thread pool in which to execute customer code. + servicer: A interfaces.Servicer for servicing the operation. + termination_manager: The _interfaces.TerminationManager for the operation. + transmission_manager: The _interfaces.TransmissionManager for the + operation. + operation_context: A interfaces.OperationContext for the operation. + emission_consumer: The _interfaces.EmissionConsumer for the operation. + + Returns: + An IngestionManager appropriate for back-side use. + """ + ingestion_manager = _IngestionManager( + lock, pool, _BackConsumerCreator( + servicer, operation_context, emission_consumer), + interfaces.Outcome.SERVICER_FAILURE, termination_manager, + transmission_manager) + return ingestion_manager diff --git a/src/python/grpcio/grpc/framework/base/_interfaces.py b/src/python/grpcio/grpc/framework/base/_interfaces.py new file mode 100644 index 0000000000..d88cf76590 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_interfaces.py @@ -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. + +"""Package-internal interfaces.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.base import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import stream + + +class TerminationManager(object): + """An object responsible for handling the termination of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this object will cooperate.""" + raise NotImplementedError() + + @abc.abstractmethod + def is_active(self): + """Reports whether or not the operation is active. + + Returns: + True if the operation is active or False if the operation has terminated. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_callback(self, callback): + """Registers a callback to be called on operation termination. + + If the operation has already terminated, the callback will be called + immediately. + + Args: + callback: A callable that will be passed an interfaces.Outcome value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def emission_complete(self): + """Indicates that emissions from customer code have completed.""" + raise NotImplementedError() + + @abc.abstractmethod + def transmission_complete(self): + """Indicates that transmissions to the remote end are complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def ingestion_complete(self): + """Indicates that customer code ingestion of received values is complete.""" + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation must abort for the indicated reason. + + Args: + outcome: An interfaces.Outcome indicating operation abortion. + """ + raise NotImplementedError() + + +class TransmissionManager(object): + """A manager responsible for transmitting to the other end of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def inmit(self, emission, complete): + """Accepts a value for transmission to the other end of the operation. + + Args: + emission: A value of some significance to the customer to be transmitted + to the other end of the operation. May be None only if complete is True. + complete: A boolean that if True indicates that customer code has emitted + all values it intends to emit. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self, outcome): + """Indicates that the operation has aborted for the indicated reason. + + Args: + outcome: An interfaces.Outcome indicating operation abortion. + """ + raise NotImplementedError() + + +class EmissionManager(stream.Consumer): + """A manager of values emitted by customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_manager_and_expiration_manager( + self, ingestion_manager, expiration_manager): + """Sets two other objects with which this EmissionManager will cooperate. + + Args: + ingestion_manager: The IngestionManager for the operation. + expiration_manager: The ExpirationManager for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, value): + """Accepts a value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates that no more values will be emitted by customer code. + + This method should only be called by customer code. + + Implementations of this method may be idempotent and forgive customer code + calling this method more than once. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Accepts the last value emitted by customer code. + + This method should only be called by customer code. + + Args: + value: Any value of significance to the customer. + """ + raise NotImplementedError() + + +class IngestionManager(stream.Consumer): + """A manager responsible for executing customer code.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_expiration_manager(self, expiration_manager): + """Sets the ExpirationManager with which this object will cooperate.""" + raise NotImplementedError() + + @abc.abstractmethod + def start(self, requirement): + """Commences execution of customer code. + + Args: + requirement: Some value unavailable at the time of this object's + construction that is required to begin executing customer code. + """ + raise NotImplementedError() + + @abc.abstractmethod + def consume(self, payload): + """Accepts a customer-significant value to be supplied to customer code. + + Args: + payload: Some customer-significant value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates the end of values to be supplied to customer code.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, payload): + """Accepts the last value to be supplied to customer code. + + Args: + payload: Some customer-significant value (and the last such value). + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ExpirationManager(object): + """A manager responsible for aborting the operation if it runs out of time.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def change_timeout(self, timeout): + """Changes the timeout allotted for the operation. + + Operation duration is always measure from the beginning of the operation; + calling this method changes the operation's allotted time to timeout total + seconds, not timeout seconds from the time of this method call. + + Args: + timeout: A length of time in seconds to allow for the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deadline(self): + """Returns the time until which the operation is allowed to run. + + Returns: + The time (seconds since the epoch) at which the operation will expire. + """ + raise NotImplementedError() + + @abc.abstractmethod + def abort(self): + """Indicates to this manager that the operation has aborted.""" + raise NotImplementedError() + + +class ReceptionManager(object): + """A manager responsible for receiving tickets from the other end.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def receive_ticket(self, ticket): + """Handle a ticket from the other side of the operation. + + Args: + ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket + appropriate to this end of the operation and this object. + """ + raise NotImplementedError() + + +class CancellationManager(object): + """A manager of operation cancellation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels the operation.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/base/_reception.py b/src/python/grpcio/grpc/framework/base/_reception.py new file mode 100644 index 0000000000..dd428964f1 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_reception.py @@ -0,0 +1,399 @@ +# 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. + +"""State and behavior for ticket reception.""" + +import abc + +from grpc.framework.base import interfaces +from grpc.framework.base import _interfaces + +_INITIAL_FRONT_TO_BACK_TICKET_KINDS = ( + interfaces.FrontToBackTicket.Kind.COMMENCEMENT, + interfaces.FrontToBackTicket.Kind.ENTIRE, +) + + +class _Receiver(object): + """Common specification of different ticket-handling behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def abort_if_abortive(self, ticket): + """Aborts the operation if the ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + A boolean indicating whether or not this Receiver aborted the operation + based on the ticket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def receive(self, ticket): + """Handles a just-arrived ticket. + + Args: + ticket: A just-arrived ticket. + + Returns: + A boolean indicating whether or not the ticket was terminal (i.e. whether + or not non-abortive tickets are legal after this one). + """ + raise NotImplementedError() + + @abc.abstractmethod + def reception_failure(self): + """Aborts the operation with an indication of reception failure.""" + raise NotImplementedError() + + +def _abort( + outcome, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Indicates abortion with the given outcome to the given managers.""" + termination_manager.abort(outcome) + transmission_manager.abort(outcome) + ingestion_manager.abort() + expiration_manager.abort() + + +def _abort_if_abortive( + ticket, abortive, termination_manager, transmission_manager, + ingestion_manager, expiration_manager): + """Determines a ticket's being abortive and if so aborts the operation. + + Args: + ticket: A just-arrived ticket. + abortive: A callable that takes a ticket and returns an interfaces.Outcome + indicating that the operation should be aborted or None indicating that + the operation should not be aborted. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + True if the operation was aborted; False otherwise. + """ + abortion_outcome = abortive(ticket) + if abortion_outcome is None: + return False + else: + _abort( + abortion_outcome, termination_manager, transmission_manager, + ingestion_manager, expiration_manager) + return True + + +def _reception_failure( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Aborts the operation with an indication of reception failure.""" + _abort( + interfaces.Outcome.RECEPTION_FAILURE, termination_manager, + transmission_manager, ingestion_manager, expiration_manager) + + +class _BackReceiver(_Receiver): + """Ticket-handling specific to the back side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._first_ticket_seen = False + self._last_ticket_seen = False + + def _abortive(self, ticket): + """Determines whether or not (and if so, how) a ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + An interfaces.Outcome value describing operation abortion if the + ticket is abortive or None if the ticket is not abortive. + """ + if ticket.kind is interfaces.FrontToBackTicket.Kind.CANCELLATION: + return interfaces.Outcome.CANCELLED + elif ticket.kind is interfaces.FrontToBackTicket.Kind.EXPIRATION: + return interfaces.Outcome.EXPIRED + elif ticket.kind is interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE: + return interfaces.Outcome.SERVICED_FAILURE + elif ticket.kind is interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE: + return interfaces.Outcome.SERVICED_FAILURE + elif (ticket.kind in _INITIAL_FRONT_TO_BACK_TICKET_KINDS and + self._first_ticket_seen): + return interfaces.Outcome.RECEPTION_FAILURE + elif self._last_ticket_seen: + return interfaces.Outcome.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, ticket): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + ticket, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, ticket): + """See _Receiver.receive for specification.""" + if ticket.timeout is not None: + self._expiration_manager.change_timeout(ticket.timeout) + + if ticket.kind is interfaces.FrontToBackTicket.Kind.COMMENCEMENT: + self._first_ticket_seen = True + self._ingestion_manager.start(ticket.name) + if ticket.payload is not None: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.FrontToBackTicket.Kind.CONTINUATION: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.FrontToBackTicket.Kind.COMPLETION: + self._last_ticket_seen = True + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + else: + self._first_ticket_seen = True + self._last_ticket_seen = True + self._ingestion_manager.start(ticket.name) + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _FrontReceiver(_Receiver): + """Ticket-handling specific to the front side of an operation.""" + + def __init__( + self, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Constructor. + + Args: + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + """ + self._termination_manager = termination_manager + self._transmission_manager = transmission_manager + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + self._last_ticket_seen = False + + def _abortive(self, ticket): + """Determines whether or not (and if so, how) a ticket is abortive. + + Args: + ticket: A just-arrived ticket. + + Returns: + An interfaces.Outcome value describing operation abortion if the ticket + is abortive or None if the ticket is not abortive. + """ + if ticket.kind is interfaces.BackToFrontTicket.Kind.CANCELLATION: + return interfaces.Outcome.CANCELLED + elif ticket.kind is interfaces.BackToFrontTicket.Kind.EXPIRATION: + return interfaces.Outcome.EXPIRED + elif ticket.kind is interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE: + return interfaces.Outcome.SERVICER_FAILURE + elif ticket.kind is interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE: + return interfaces.Outcome.SERVICER_FAILURE + elif self._last_ticket_seen: + return interfaces.Outcome.RECEPTION_FAILURE + else: + return None + + def abort_if_abortive(self, ticket): + """See _Receiver.abort_if_abortive for specification.""" + return _abort_if_abortive( + ticket, self._abortive, self._termination_manager, + self._transmission_manager, self._ingestion_manager, + self._expiration_manager) + + def receive(self, ticket): + """See _Receiver.receive for specification.""" + if ticket.kind is interfaces.BackToFrontTicket.Kind.CONTINUATION: + self._ingestion_manager.consume(ticket.payload) + elif ticket.kind is interfaces.BackToFrontTicket.Kind.COMPLETION: + self._last_ticket_seen = True + if ticket.payload is None: + self._ingestion_manager.terminate() + else: + self._ingestion_manager.consume_and_terminate(ticket.payload) + + def reception_failure(self): + """See _Receiver.reception_failure for specification.""" + _reception_failure( + self._termination_manager, self._transmission_manager, + self._ingestion_manager, self._expiration_manager) + + +class _ReceptionManager(_interfaces.ReceptionManager): + """A ReceptionManager based around a _Receiver passed to it.""" + + def __init__(self, lock, receiver): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + receiver: A _Receiver responsible for handling received tickets. + """ + self._lock = lock + self._receiver = receiver + + self._lowest_unseen_sequence_number = 0 + self._out_of_sequence_tickets = {} + self._completed_sequence_number = None + self._aborted = False + + def _sequence_failure(self, ticket): + """Determines a just-arrived ticket's sequential legitimacy. + + Args: + ticket: A just-arrived ticket. + + Returns: + True if the ticket is sequentially legitimate; False otherwise. + """ + if ticket.sequence_number < self._lowest_unseen_sequence_number: + return True + elif ticket.sequence_number in self._out_of_sequence_tickets: + return True + elif (self._completed_sequence_number is not None and + self._completed_sequence_number <= ticket.sequence_number): + return True + else: + return False + + def _process(self, ticket): + """Process those tickets ready to be processed. + + Args: + ticket: A just-arrived ticket the sequence number of which matches this + _ReceptionManager's _lowest_unseen_sequence_number field. + """ + while True: + completed = self._receiver.receive(ticket) + if completed: + self._out_of_sequence_tickets.clear() + self._completed_sequence_number = ticket.sequence_number + self._lowest_unseen_sequence_number = ticket.sequence_number + 1 + return + else: + next_ticket = self._out_of_sequence_tickets.pop( + ticket.sequence_number + 1, None) + if next_ticket is None: + self._lowest_unseen_sequence_number = ticket.sequence_number + 1 + return + else: + ticket = next_ticket + + def receive_ticket(self, ticket): + """See _interfaces.ReceptionManager.receive_ticket for specification.""" + with self._lock: + if self._aborted: + return + elif self._sequence_failure(ticket): + self._receiver.reception_failure() + self._aborted = True + elif self._receiver.abort_if_abortive(ticket): + self._aborted = True + elif ticket.sequence_number == self._lowest_unseen_sequence_number: + self._process(ticket) + else: + self._out_of_sequence_tickets[ticket.sequence_number] = ticket + + +def front_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for front-side use. + """ + return _ReceptionManager( + lock, _FrontReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) + + +def back_reception_manager( + lock, termination_manager, transmission_manager, ingestion_manager, + expiration_manager): + """Creates a _interfaces.ReceptionManager for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + termination_manager: The operation's _interfaces.TerminationManager. + transmission_manager: The operation's _interfaces.TransmissionManager. + ingestion_manager: The operation's _interfaces.IngestionManager. + expiration_manager: The operation's _interfaces.ExpirationManager. + + Returns: + A _interfaces.ReceptionManager appropriate for back-side use. + """ + return _ReceptionManager( + lock, _BackReceiver( + termination_manager, transmission_manager, ingestion_manager, + expiration_manager)) diff --git a/src/python/grpcio/grpc/framework/base/_termination.py b/src/python/grpcio/grpc/framework/base/_termination.py new file mode 100644 index 0000000000..ddcbc60293 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_termination.py @@ -0,0 +1,204 @@ +# 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. + +"""State and behavior for operation termination.""" + +import enum + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' + + +@enum.unique +class _Requirement(enum.Enum): + """Symbols indicating events required for termination.""" + + EMISSION = 'emission' + TRANSMISSION = 'transmission' + INGESTION = 'ingestion' + +_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,) +_BACK_NOT_LISTENING_REQUIREMENTS = ( + _Requirement.EMISSION, _Requirement.INGESTION,) +_LISTENING_REQUIREMENTS = ( + _Requirement.TRANSMISSION, _Requirement.INGESTION,) + + +class _TerminationManager(_interfaces.TerminationManager): + """An implementation of _interfaces.TerminationManager.""" + + def __init__( + self, work_pool, utility_pool, action, requirements, local_failure): + """Constructor. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + requirements: A combination of _Requirement values identifying what + must finish for the operation to be considered completed. + local_failure: An interfaces.Outcome specifying what constitutes local + failure of customer work. + """ + self._work_pool = work_pool + self._utility_pool = utility_pool + self._action = action + self._local_failure = local_failure + self._has_locally_failed = False + self._expiration_manager = None + + self._outstanding_requirements = set(requirements) + self._outcome = None + self._callbacks = [] + + def set_expiration_manager(self, expiration_manager): + self._expiration_manager = expiration_manager + + def _terminate(self, outcome): + """Terminates the operation. + + Args: + outcome: An interfaces.Outcome describing the outcome of the operation. + """ + self._expiration_manager.abort() + self._outstanding_requirements = None + callbacks = list(self._callbacks) + self._callbacks = None + self._outcome = outcome + + act = callable_util.with_exceptions_logged( + self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) + + if self._has_locally_failed: + self._utility_pool.submit(act, outcome) + else: + def call_callbacks_and_act(callbacks, outcome): + for callback in callbacks: + callback_outcome = callable_util.call_logging_exceptions( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) + if callback_outcome.exception is not None: + outcome = self._local_failure + break + self._utility_pool.submit(act, outcome) + + self._work_pool.submit(callable_util.with_exceptions_logged( + call_callbacks_and_act, + _constants.INTERNAL_ERROR_LOG_MESSAGE), + callbacks, outcome) + + def is_active(self): + """See _interfaces.TerminationManager.is_active for specification.""" + return self._outstanding_requirements is not None + + def add_callback(self, callback): + """See _interfaces.TerminationManager.add_callback for specification.""" + if not self._has_locally_failed: + if self._outstanding_requirements is None: + self._work_pool.submit( + callable_util.with_exceptions_logged( + callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), self._outcome) + else: + self._callbacks.append(callback) + + def emission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.EMISSION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def transmission_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.TRANSMISSION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def ingestion_complete(self): + """See superclass method for specification.""" + if self._outstanding_requirements is not None: + self._outstanding_requirements.discard(_Requirement.INGESTION) + if not self._outstanding_requirements: + self._terminate(interfaces.Outcome.COMPLETED) + + def abort(self, outcome): + """See _interfaces.TerminationManager.abort for specification.""" + if outcome is self._local_failure: + self._has_failed_locally = True + if self._outstanding_requirements is not None: + self._terminate(outcome) + + +def front_termination_manager( + work_pool, utility_pool, action, subscription_kind): + """Creates a TerminationManager appropriate for front-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription_kind: An interfaces.ServicedSubscription.Kind value. + + Returns: + A TerminationManager appropriate for front-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + requirements = _FRONT_NOT_LISTENING_REQUIREMENTS + else: + requirements = _LISTENING_REQUIREMENTS + + return _TerminationManager( + work_pool, utility_pool, action, requirements, + interfaces.Outcome.SERVICED_FAILURE) + + +def back_termination_manager(work_pool, utility_pool, action, subscription_kind): + """Creates a TerminationManager appropriate for back-side use. + + Args: + work_pool: A thread pool in which customer work will be done. + utility_pool: A thread pool in which work utility work will be done. + action: An action to call on operation termination. + subscription_kind: An interfaces.ServicedSubscription.Kind value. + + Returns: + A TerminationManager appropriate for back-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + requirements = _BACK_NOT_LISTENING_REQUIREMENTS + else: + requirements = _LISTENING_REQUIREMENTS + + return _TerminationManager( + work_pool, utility_pool, action, requirements, + interfaces.Outcome.SERVICER_FAILURE) diff --git a/src/python/grpcio/grpc/framework/base/_transmission.py b/src/python/grpcio/grpc/framework/base/_transmission.py new file mode 100644 index 0000000000..6845129234 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/_transmission.py @@ -0,0 +1,429 @@ +# 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. + +"""State and behavior for ticket transmission during an operation.""" + +import abc + +from grpc.framework.base import _constants +from grpc.framework.base import _interfaces +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + +_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' + +_FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES = ( + interfaces.Outcome.SERVICER_FAILURE, + ) +_BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES = ( + interfaces.Outcome.CANCELLED, + interfaces.Outcome.SERVICED_FAILURE, + ) + +_ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND = { + interfaces.Outcome.CANCELLED: + interfaces.FrontToBackTicket.Kind.CANCELLATION, + interfaces.Outcome.EXPIRED: + interfaces.FrontToBackTicket.Kind.EXPIRATION, + interfaces.Outcome.RECEPTION_FAILURE: + interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE, + interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, + interfaces.Outcome.SERVICED_FAILURE: + interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE, + interfaces.Outcome.SERVICER_FAILURE: + interfaces.FrontToBackTicket.Kind.SERVICER_FAILURE, +} + +_ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND = { + interfaces.Outcome.CANCELLED: + interfaces.BackToFrontTicket.Kind.CANCELLATION, + interfaces.Outcome.EXPIRED: + interfaces.BackToFrontTicket.Kind.EXPIRATION, + interfaces.Outcome.RECEPTION_FAILURE: + interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE, + interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, + interfaces.Outcome.SERVICED_FAILURE: + interfaces.BackToFrontTicket.Kind.SERVICED_FAILURE, + interfaces.Outcome.SERVICER_FAILURE: + interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE, +} + + +class _Ticketizer(object): + """Common specification of different ticket-creating behavior.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def ticketize(self, operation_id, sequence_number, payload, complete): + """Creates a ticket indicating ordinary operation progress. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the ticket. + payload: A customer payload object. May be None if sequence_number is + zero or complete is true. + complete: A boolean indicating whether or not the ticket should describe + itself as (but for a later indication of operation abortion) the last + ticket to be sent. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation. + """ + raise NotImplementedError() + + @abc.abstractmethod + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """Creates a ticket indicating that the operation is aborted. + + Args: + operation_id: The operation ID for the current operation. + sequence_number: A sequence number for the ticket. + outcome: An interfaces.Outcome value describing the operation abortion. + + Returns: + An object of an appropriate type suitable for transmission to the other + side of the operation, or None if transmission is not appropriate for + the given outcome. + """ + raise NotImplementedError() + + +class _FrontTicketizer(_Ticketizer): + """Front-side ticket-creating behavior.""" + + def __init__(self, name, subscription_kind, trace_id, timeout): + """Constructor. + + Args: + name: The name of the operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + """ + self._name = name + self._subscription_kind = subscription_kind + self._trace_id = trace_id + self._timeout = timeout + + def ticketize(self, operation_id, sequence_number, payload, complete): + """See _Ticketizer.ticketize for specification.""" + if sequence_number: + if complete: + kind = interfaces.FrontToBackTicket.Kind.COMPLETION + else: + kind = interfaces.FrontToBackTicket.Kind.CONTINUATION + return interfaces.FrontToBackTicket( + operation_id, sequence_number, kind, self._name, + self._subscription_kind, self._trace_id, payload, self._timeout) + else: + if complete: + kind = interfaces.FrontToBackTicket.Kind.ENTIRE + else: + kind = interfaces.FrontToBackTicket.Kind.COMMENCEMENT + return interfaces.FrontToBackTicket( + operation_id, 0, kind, self._name, self._subscription_kind, + self._trace_id, payload, self._timeout) + + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """See _Ticketizer.ticketize_abortion for specification.""" + if outcome in _FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES: + return None + else: + kind = _ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND[outcome] + return interfaces.FrontToBackTicket( + operation_id, sequence_number, kind, None, None, None, None, None) + + +class _BackTicketizer(_Ticketizer): + """Back-side ticket-creating behavior.""" + + def ticketize(self, operation_id, sequence_number, payload, complete): + """See _Ticketizer.ticketize for specification.""" + if complete: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + return interfaces.BackToFrontTicket( + operation_id, sequence_number, kind, payload) + + def ticketize_abortion(self, operation_id, sequence_number, outcome): + """See _Ticketizer.ticketize_abortion for specification.""" + if outcome in _BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES: + return None + else: + kind = _ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND[outcome] + return interfaces.BackToFrontTicket( + operation_id, sequence_number, kind, None) + + +class TransmissionManager(_interfaces.TransmissionManager): + """A _interfaces.TransmissionManager on which other managers may be set.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """Sets two of the other managers with which this manager may interact. + + Args: + ingestion_manager: The _interfaces.IngestionManager associated with the + current operation. + expiration_manager: The _interfaces.ExpirationManager associated with the + current operation. + """ + raise NotImplementedError() + + +class _EmptyTransmissionManager(TransmissionManager): + """A completely no-operative _interfaces.TransmissionManager.""" + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overriden method for specification.""" + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + + def abort(self, outcome): + """See _interfaces.TransmissionManager.abort for specification.""" + + +class _TransmittingTransmissionManager(TransmissionManager): + """A TransmissionManager implementation that sends tickets.""" + + def __init__( + self, lock, pool, callback, operation_id, ticketizer, + termination_manager): + """Constructor. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + ticketizer: A _Ticketizer for ticket creation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + """ + self._lock = lock + self._pool = pool + self._callback = callback + self._operation_id = operation_id + self._ticketizer = ticketizer + self._termination_manager = termination_manager + self._ingestion_manager = None + self._expiration_manager = None + + self._emissions = [] + self._emission_complete = False + self._outcome = None + self._lowest_unused_sequence_number = 0 + self._transmitting = False + + def set_ingestion_and_expiration_managers( + self, ingestion_manager, expiration_manager): + """See overridden method for specification.""" + self._ingestion_manager = ingestion_manager + self._expiration_manager = expiration_manager + + def _lead_ticket(self, emission, complete): + """Creates a ticket suitable for leading off the transmission loop. + + Args: + emission: A customer payload object to be sent to the other side of the + operation. + complete: Whether or not the sequence of customer payloads ends with + the passed object. + + Returns: + A ticket with which to lead off the transmission loop. + """ + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return self._ticketizer.ticketize( + self._operation_id, sequence_number, emission, complete) + + def _abortive_response_ticket(self, outcome): + """Creates a ticket indicating operation abortion. + + Args: + outcome: An interfaces.Outcome value describing operation abortion. + + Returns: + A ticket indicating operation abortion. + """ + ticket = self._ticketizer.ticketize_abortion( + self._operation_id, self._lowest_unused_sequence_number, outcome) + if ticket is None: + return None + else: + self._lowest_unused_sequence_number += 1 + return ticket + + def _next_ticket(self): + """Creates the next ticket to be sent to the other side of the operation. + + Returns: + A (completed, ticket) tuple comprised of a boolean indicating whether or + not the sequence of tickets has completed normally and a ticket to send + to the other side if the sequence of tickets hasn't completed. The tuple + will never have both a True first element and a non-None second element. + """ + if self._emissions is None: + return False, None + elif self._outcome is None: + if self._emissions: + payload = self._emissions.pop(0) + complete = self._emission_complete and not self._emissions + sequence_number = self._lowest_unused_sequence_number + self._lowest_unused_sequence_number += 1 + return complete, self._ticketizer.ticketize( + self._operation_id, sequence_number, payload, complete) + else: + return self._emission_complete, None + else: + ticket = self._abortive_response_ticket(self._outcome) + self._emissions = None + return False, None if ticket is None else ticket + + def _transmit(self, ticket): + """Commences the transmission loop sending tickets. + + Args: + ticket: A ticket to be sent to the other side of the operation. + """ + def transmit(ticket): + while True: + transmission_outcome = callable_util.call_logging_exceptions( + self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket) + if transmission_outcome.exception is None: + with self._lock: + complete, ticket = self._next_ticket() + if ticket is None: + if complete: + self._termination_manager.transmission_complete() + self._transmitting = False + return + else: + with self._lock: + self._emissions = None + self._termination_manager.abort( + interfaces.Outcome.TRANSMISSION_FAILURE) + self._ingestion_manager.abort() + self._expiration_manager.abort() + self._transmitting = False + return + + self._pool.submit(callable_util.with_exceptions_logged( + transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket) + self._transmitting = True + + def inmit(self, emission, complete): + """See _interfaces.TransmissionManager.inmit for specification.""" + if self._emissions is not None and self._outcome is None: + self._emission_complete = complete + if self._transmitting: + self._emissions.append(emission) + else: + self._transmit(self._lead_ticket(emission, complete)) + + def abort(self, outcome): + """See _interfaces.TransmissionManager.abort for specification.""" + if self._emissions is not None and self._outcome is None: + self._outcome = outcome + if not self._transmitting: + ticket = self._abortive_response_ticket(outcome) + self._emissions = None + if ticket is not None: + self._transmit(ticket) + + +def front_transmission_manager( + lock, pool, callback, operation_id, name, subscription_kind, trace_id, + timeout, termination_manager): + """Creates a TransmissionManager appropriate for front-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + name: The name of the operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + timeout: A length of time in seconds to allow for the entire operation. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + + Returns: + A TransmissionManager appropriate for front-side use. + """ + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _FrontTicketizer( + name, subscription_kind, trace_id, timeout), + termination_manager) + + +def back_transmission_manager( + lock, pool, callback, operation_id, termination_manager, + subscription_kind): + """Creates a TransmissionManager appropriate for back-side use. + + Args: + lock: The operation-servicing-wide lock object. + pool: A thread pool in which the work of transmitting tickets will be + performed. + callback: A callable that accepts tickets and sends them to the other side + of the operation. + operation_id: The operation's ID. + termination_manager: The _interfaces.TerminationManager associated with + this operation. + subscription_kind: An interfaces.ServicedSubscription.Kind value + describing the interest the front has in tickets sent from the back. + + Returns: + A TransmissionManager appropriate for back-side use. + """ + if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: + return _EmptyTransmissionManager() + else: + return _TransmittingTransmissionManager( + lock, pool, callback, operation_id, _BackTicketizer(), + termination_manager) diff --git a/src/python/grpcio/grpc/framework/base/exceptions.py b/src/python/grpcio/grpc/framework/base/exceptions.py new file mode 100644 index 0000000000..b8f4752184 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/exceptions.py @@ -0,0 +1,34 @@ +# 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. + +"""Exceptions defined and used by the base layer of RPC Framework.""" + + +class NoSuchMethodError(Exception): + """Indicates that an operation with an unrecognized name has been called.""" diff --git a/src/python/grpcio/grpc/framework/base/implementations.py b/src/python/grpcio/grpc/framework/base/implementations.py new file mode 100644 index 0000000000..5656f9f981 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/implementations.py @@ -0,0 +1,77 @@ +# 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. + +"""Entry points into the ticket-exchange-based base layer implementation.""" + +# interfaces is referenced from specification in this module. +from grpc.framework.base import _ends +from grpc.framework.base import interfaces # pylint: disable=unused-import + + +def front_link(work_pool, transmission_pool, utility_pool): + """Factory function for creating interfaces.FrontLinks. + + Args: + work_pool: A thread pool to be used for doing work within the created + FrontLink object. + transmission_pool: A thread pool to be used within the created FrontLink + object for transmitting values to a joined RearLink object. + utility_pool: A thread pool to be used within the created FrontLink object + for utility tasks. + + Returns: + An interfaces.FrontLink. + """ + return _ends.FrontLink(work_pool, transmission_pool, utility_pool) + + +def back_link( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout): + """Factory function for creating interfaces.BackLinks. + + Args: + servicer: An interfaces.Servicer for servicing operations. + work_pool: A thread pool to be used for doing work within the created + BackLink object. + transmission_pool: A thread pool to be used within the created BackLink + object for transmitting values to a joined ForeLink object. + utility_pool: A thread pool to be used within the created BackLink object + for utility tasks. + default_timeout: A length of time in seconds to be used as the default + time alloted for a single operation. + maximum_timeout: A length of time in seconds to be used as the maximum + time alloted for a single operation. + + Returns: + An interfaces.BackLink. + """ + return _ends.BackLink( + servicer, work_pool, transmission_pool, utility_pool, default_timeout, + maximum_timeout) diff --git a/src/python/grpcio/grpc/framework/base/implementations_test.py b/src/python/grpcio/grpc/framework/base/implementations_test.py new file mode 100644 index 0000000000..72087f4456 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/implementations_test.py @@ -0,0 +1,80 @@ +# 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 grpc.framework.base.implementations.""" + +import unittest + +from grpc.framework.base import implementations +from grpc.framework.base import interfaces_test_case +from grpc.framework.base import util +from grpc.framework.foundation import logging_pool + +POOL_MAX_WORKERS = 10 +DEFAULT_TIMEOUT = 30 +MAXIMUM_TIMEOUT = 60 + + +class ImplementationsTest( + interfaces_test_case.FrontAndBackTest, unittest.TestCase): + + def setUp(self): + self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) + self.front = implementations.front_link( + self.front_work_pool, self.front_transmission_pool, + self.front_utility_pool) + self.back = implementations.back_link( + self.test_servicer, self.back_work_pool, self.back_transmission_pool, + self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) + self.front.join_rear_link(self.back) + self.back.join_fore_link(self.front) + + def tearDown(self): + util.wait_for_idle(self.back) + util.wait_for_idle(self.front) + self.memory_transmission_pool.shutdown(wait=True) + self.front_work_pool.shutdown(wait=True) + self.front_transmission_pool.shutdown(wait=True) + self.front_utility_pool.shutdown(wait=True) + self.back_work_pool.shutdown(wait=True) + self.back_transmission_pool.shutdown(wait=True) + self.back_utility_pool.shutdown(wait=True) + self.test_pool.shutdown(wait=True) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/base/in_memory.py b/src/python/grpcio/grpc/framework/base/in_memory.py new file mode 100644 index 0000000000..c92d0bc663 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/in_memory.py @@ -0,0 +1,108 @@ +# 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. + +"""In-memory implementations of base layer interfaces.""" + +import threading + +from grpc.framework.base import _constants +from grpc.framework.base import interfaces +from grpc.framework.foundation import callable_util + + +class _Serializer(object): + """A utility for serializing values that may arrive concurrently.""" + + def __init__(self, pool): + self._lock = threading.Lock() + self._pool = pool + self._sink = None + self._spinning = False + self._values = [] + + def _spin(self, sink, value): + while True: + sink(value) + with self._lock: + if self._sink is None or not self._values: + self._spinning = False + return + else: + sink, value = self._sink, self._values.pop(0) + + def set_sink(self, sink): + with self._lock: + self._sink = sink + if sink is not None and self._values and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + sink, self._values.pop(0)) + + def add_value(self, value): + with self._lock: + if self._sink and not self._spinning: + self._spinning = True + self._pool.submit( + callable_util.with_exceptions_logged( + self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), + self._sink, value) + else: + self._values.append(value) + + +class Link(interfaces.ForeLink, interfaces.RearLink): + """A trivial implementation of interfaces.ForeLink and interfaces.RearLink.""" + + def __init__(self, pool): + """Constructor. + + Args: + pool: A thread pool to be used for serializing ticket exchange in each + direction. + """ + self._front_to_back = _Serializer(pool) + self._back_to_front = _Serializer(pool) + + def join_fore_link(self, fore_link): + """See interfaces.RearLink.join_fore_link for specification.""" + self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket) + + def join_rear_link(self, rear_link): + """See interfaces.ForeLink.join_rear_link for specification.""" + self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket) + + def accept_front_to_back_ticket(self, ticket): + """See interfaces.ForeLink.accept_front_to_back_ticket for specification.""" + self._front_to_back.add_value(ticket) + + def accept_back_to_front_ticket(self, ticket): + """See interfaces.RearLink.accept_back_to_front_ticket for specification.""" + self._back_to_front.add_value(ticket) diff --git a/src/python/grpcio/grpc/framework/base/interfaces.py b/src/python/grpcio/grpc/framework/base/interfaces.py new file mode 100644 index 0000000000..e22c10d975 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/interfaces.py @@ -0,0 +1,363 @@ +# 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. + +"""Interfaces defined and used by the base layer of RPC Framework.""" + +import abc +import collections +import enum + +# stream is referenced from specification in this module. +from grpc.framework.foundation import stream # pylint: disable=unused-import + + +@enum.unique +class Outcome(enum.Enum): + """Operation outcomes.""" + + COMPLETED = 'completed' + CANCELLED = 'cancelled' + EXPIRED = 'expired' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + + +class OperationContext(object): + """Provides operation-related information and action. + + Attributes: + trace_id: A uuid.UUID identifying a particular set of related operations. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the operation is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def add_termination_callback(self, callback): + """Adds a function to be called upon operation termination. + + Args: + callback: A callable that will be passed an Outcome value. + """ + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the operation. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the operation to complete before it is considered to have + timed out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def fail(self, exception): + """Indicates that the operation has failed. + + Args: + exception: An exception germane to the operation failure. May be None. + """ + raise NotImplementedError() + + +class Servicer(object): + """Interface for service implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, context, output_consumer): + """Services an operation. + + Args: + name: The name of the operation. + context: A ServicerContext object affording contextual information and + actions. + output_consumer: A stream.Consumer that will accept output values of + the operation. + + Returns: + A stream.Consumer that will accept input values for the operation. + + Raises: + exceptions.NoSuchMethodError: If this Servicer affords no method with the + given name. + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class Operation(object): + """Representation of an in-progress operation. + + Attributes: + consumer: A stream.Consumer into which payloads constituting the operation's + input may be passed. + context: An OperationContext affording information and action about the + operation. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Cancels this operation.""" + raise NotImplementedError() + + +class ServicedIngestor(object): + """Responsible for accepting the result of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consumer(self, operation_context): + """Affords a consumer to which operation results will be passed. + + Args: + operation_context: An OperationContext object for the current operation. + + Returns: + A stream.Consumer to which the results of the current operation will be + passed. + + Raises: + abandonment.Abandoned: If the operation has been aborted and there no + longer is any reason to service the operation. + """ + raise NotImplementedError() + + +class ServicedSubscription(object): + """A sum type representing a serviced's interest in an operation. + + Attributes: + kind: A Kind value. + ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must + be None if kind is Kind.TERMINATION_ONLY or Kind.NONE. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + """Kinds of subscription.""" + + FULL = 'full' + TERMINATION_ONLY = 'termination only' + NONE = 'none' + + +class End(object): + """Common type for entry-point objects on both sides of an operation.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operation_stats(self): + """Reports the number of terminated operations broken down by outcome. + + Returns: + A dictionary from Outcome value to an integer identifying the number + of operations that terminated with that outcome. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_idle_action(self, action): + """Adds an action to be called when this End has no ongoing operations. + + Args: + action: A callable that accepts no arguments. + """ + raise NotImplementedError() + + +class Front(End): + """Clientish objects that afford the invocation of operations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def operate( + self, name, payload, complete, timeout, subscription, trace_id): + """Commences an operation. + + Args: + name: The name of the method invoked for the operation. + payload: An initial payload for the operation. May be None. + complete: A boolean indicating whether or not additional payloads to be + sent to the servicer may be supplied after this call. + timeout: A length of time in seconds to allow for the operation. + subscription: A ServicedSubscription for the operation. + trace_id: A uuid.UUID identifying a set of related operations to which + this operation belongs. + + Returns: + An Operation object affording information and action about the operation + in progress. + """ + raise NotImplementedError() + + +class Back(End): + """Serverish objects that perform the work of operations.""" + __metaclass__ = abc.ABCMeta + + +class FrontToBackTicket( + collections.namedtuple( + 'FrontToBackTicket', + ['operation_id', 'sequence_number', 'kind', 'name', 'subscription', + 'trace_id', 'payload', 'timeout'])): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place among all the tickets sent from front to back for this + particular operation. Must be zero if kind is Kind.COMMENCEMENT or + Kind.ENTIRE. Must be positive for any other kind. + kind: A Kind value describing the overall kind of ticket. + name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT + or Kind.ENTIRE. Must be None for any other kind. + subscription: An ServicedSubscription.Kind value describing the interest + the front has in tickets sent from the back. Must be present if + kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind. + trace_id: A uuid.UUID identifying a set of related operations to which this + operation belongs. May be None. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None + for any other kind. + timeout: An optional length of time (measured from the beginning of the + operation) to allow for the entire operation. If None, a default value on + the back will be used. If present and excessively large, the back may + limit the operation to a smaller duration of its choice. May be present + for any ticket kind; setting a value on a later ticket allows fronts + to request time extensions (or even time reductions!) on in-progress + operations. + """ + + @enum.unique + class Kind(enum.Enum): + """Identifies the overall kind of a FrontToBackTicket.""" + + COMMENCEMENT = 'commencement' + CONTINUATION = 'continuation' + COMPLETION = 'completion' + ENTIRE = 'entire' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + + +class BackToFrontTicket( + collections.namedtuple( + 'BackToFrontTicket', + ['operation_id', 'sequence_number', 'kind', 'payload'])): + """A sum type for all values sent from a back to a front. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place among all the tickets sent from back to front for this + particular operation. + kind: A Kind value describing the overall kind of ticket. + payload: A customer payload object. Must be present if kind is + Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None + otherwise. + """ + + @enum.unique + class Kind(enum.Enum): + """Identifies the overall kind of a BackToFrontTicket.""" + + CONTINUATION = 'continuation' + COMPLETION = 'completion' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + SERVICER_FAILURE = 'servicer failure' + SERVICED_FAILURE = 'serviced failure' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + + +class ForeLink(object): + """Accepts back-to-front tickets and emits front-to-back tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_back_to_front_ticket(self, ticket): + """Accept a BackToFrontTicket. + + Args: + ticket: Any BackToFrontTicket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_rear_link(self, rear_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class RearLink(object): + """Accepts front-to-back tickets and emits back-to-front tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_front_to_back_ticket(self, ticket): + """Accepts a FrontToBackTicket. + + Args: + ticket: Any FrontToBackTicket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_fore_link(self, fore_link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() + + +class FrontLink(Front, ForeLink): + """Clientish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta + + +class BackLink(Back, RearLink): + """Serverish objects that operate by sending and receiving tickets.""" + __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/base/interfaces_test_case.py b/src/python/grpcio/grpc/framework/base/interfaces_test_case.py new file mode 100644 index 0000000000..dec10c2924 --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/interfaces_test_case.py @@ -0,0 +1,307 @@ +# 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. + +"""Abstract tests against the interfaces of the base layer of RPC Framework.""" + +import threading +import time + +from grpc.framework.base import interfaces +from grpc.framework.base import util +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_testing +from grpc.framework.foundation import stream_util + +TICK = 0.1 +SMALL_TIMEOUT = TICK * 50 +STREAM_LENGTH = 100 + +SYNCHRONOUS_ECHO = 'synchronous echo' +ASYNCHRONOUS_ECHO = 'asynchronous echo' +IMMEDIATE_FAILURE = 'immediate failure' +TRIGGERED_FAILURE = 'triggered failure' +WAIT_ON_CONDITION = 'wait on condition' + +EMPTY_OUTCOME_DICT = { + interfaces.Outcome.COMPLETED: 0, + interfaces.Outcome.CANCELLED: 0, + interfaces.Outcome.EXPIRED: 0, + interfaces.Outcome.RECEPTION_FAILURE: 0, + interfaces.Outcome.TRANSMISSION_FAILURE: 0, + interfaces.Outcome.SERVICER_FAILURE: 0, + interfaces.Outcome.SERVICED_FAILURE: 0, + } + + +def _synchronous_echo(output_consumer): + return stream_util.TransformingConsumer(lambda x: x, output_consumer) + + +class AsynchronousEcho(stream.Consumer): + """A stream.Consumer that echoes its input to another stream.Consumer.""" + + def __init__(self, output_consumer, pool): + self._lock = threading.Lock() + self._output_consumer = output_consumer + self._pool = pool + + self._queue = [] + self._spinning = False + + def _spin(self, value, complete): + while True: + if value: + if complete: + self._output_consumer.consume_and_terminate(value) + else: + self._output_consumer.consume(value) + elif complete: + self._output_consumer.terminate() + with self._lock: + if self._queue: + value, complete = self._queue.pop(0) + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, False)) + else: + self._spinning = True + self._pool.submit(self._spin, value, False) + + def terminate(self): + with self._lock: + if self._spinning: + self._queue.append((None, True)) + else: + self._spinning = True + self._pool.submit(self._spin, None, True) + + def consume_and_terminate(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, True)) + else: + self._spinning = True + self._pool.submit(self._spin, value, True) + + +class TestServicer(interfaces.Servicer): + """An interfaces.Servicer with instrumented for testing.""" + + def __init__(self, pool): + self._pool = pool + self.condition = threading.Condition() + self._released = False + + def service(self, name, context, output_consumer): + if name == SYNCHRONOUS_ECHO: + return _synchronous_echo(output_consumer) + elif name == ASYNCHRONOUS_ECHO: + return AsynchronousEcho(output_consumer, self._pool) + elif name == IMMEDIATE_FAILURE: + raise ValueError() + elif name == TRIGGERED_FAILURE: + raise NotImplementedError + elif name == WAIT_ON_CONDITION: + with self.condition: + while not self._released: + self.condition.wait() + return _synchronous_echo(output_consumer) + else: + raise NotImplementedError() + + def release(self): + with self.condition: + self._released = True + self.condition.notify_all() + + +class EasyServicedIngestor(interfaces.ServicedIngestor): + """A trivial implementation of interfaces.ServicedIngestor.""" + + def __init__(self, consumer): + self._consumer = consumer + + def consumer(self, operation_context): + """See interfaces.ServicedIngestor.consumer for specification.""" + return self._consumer + + +class FrontAndBackTest(object): + """A test suite usable against any joined Front and Back.""" + + # Pylint doesn't know that this is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + def testSimplestCall(self): + """Tests the absolute simplest call - a one-ticket fire-and-forget.""" + self.front.operate( + SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, + util.none_serviced_subscription(), 'test trace ID') + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) The ticket is still either in the front waiting to be transmitted + # or is somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the ticket bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) The ticket arrived at the back and the back completed the operation. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.COMPLETED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + # It's true that if the ticket had arrived at the back and the back had + # begun processing that wait_for_idle could hold test execution until the + # back completed the operation, but that doesn't really collapse the + # possibility space down to one solution. + + def testEntireEcho(self): + """Tests a very simple one-ticket-each-way round-trip.""" + test_payload = 'test payload' + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + self.front.operate( + ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, + 'test trace ID') + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual([(test_payload, True)], test_consumer.calls) + + def testBidirectionalStreamingEcho(self): + """Tests sending multiple tickets each way.""" + test_payload_template = 'test_payload: %03d' + test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + + for test_payload in test_payloads: + operation.consumer.consume(test_payload) + operation.consumer.terminate() + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual(test_payloads, test_consumer.values()) + + def testCancellation(self): + """Tests cancelling a long-lived operation.""" + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + operation.cancel() + + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) + util.wait_for_idle(self.back) + self.assertListEqual([], test_consumer.calls) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) Both tickets are still either in the front waiting to be transmitted + # or are somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the tickets bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. + # The back started processing based on the first ticket and then stopped + # upon receiving the cancellation ticket. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.CANCELLED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + + def testExpiration(self): + """Tests that operations time out.""" + timeout = TICK * 2 + allowance = TICK # How much extra time to + condition = threading.Condition() + test_payload = 'test payload' + subscription = util.termination_only_serviced_subscription() + start_time = time.time() + + outcome_cell = [None] + termination_time_cell = [None] + def termination_action(outcome): + with condition: + outcome_cell[0] = outcome + termination_time_cell[0] = time.time() + condition.notify() + + with condition: + operation = self.front.operate( + SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, + 'test trace ID') + operation.context.add_termination_callback(termination_action) + while outcome_cell[0] is None: + condition.wait() + + duration = termination_time_cell[0] - start_time + self.assertLessEqual(timeout, duration) + self.assertLess(duration, timeout + allowance) + self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) + util.wait_for_idle(self.back) + self.assertLessEqual( + 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/grpcio/grpc/framework/base/null.py b/src/python/grpcio/grpc/framework/base/null.py new file mode 100644 index 0000000000..1e30d4557b --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/null.py @@ -0,0 +1,56 @@ +# 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. + +"""Null links that ignore tickets passed to them.""" + +from grpc.framework.base import interfaces + + +class _NullForeLink(interfaces.ForeLink): + """A do-nothing ForeLink.""" + + def accept_back_to_front_ticket(self, ticket): + pass + + def join_rear_link(self, rear_link): + raise NotImplementedError() + + +class _NullRearLink(interfaces.RearLink): + """A do-nothing RearLink.""" + + def accept_front_to_back_ticket(self, ticket): + pass + + def join_fore_link(self, fore_link): + raise NotImplementedError() + + +NULL_FORE_LINK = _NullForeLink() +NULL_REAR_LINK = _NullRearLink() diff --git a/src/python/grpcio/grpc/framework/base/util.py b/src/python/grpcio/grpc/framework/base/util.py new file mode 100644 index 0000000000..c832c826cf --- /dev/null +++ b/src/python/grpcio/grpc/framework/base/util.py @@ -0,0 +1,94 @@ +# 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. + +"""Utilities helpful for working with the base layer of RPC Framework.""" + +import collections +import threading + +from grpc.framework.base import interfaces + + +class _ServicedSubscription( + collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']), + interfaces.ServicedSubscription): + """See interfaces.ServicedSubscription for specification.""" + +_NONE_SUBSCRIPTION = _ServicedSubscription( + interfaces.ServicedSubscription.Kind.NONE, None) +_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( + interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None) + + +def none_serviced_subscription(): + """Creates a "none" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating no subscription to an + operation's results (such as would be the case for a fire-and-forget + operation invocation). + """ + return _NONE_SUBSCRIPTION + + +def termination_only_serviced_subscription(): + """Creates a "termination only" interfaces.ServicedSubscription object. + + Returns: + An interfaces.ServicedSubscription indicating that the front-side customer + is interested only in the overall termination outcome of the operation + (such as completion or expiration) and would ignore the actual results of + the operation. + """ + return _TERMINATION_ONLY_SUBSCRIPTION + + +def full_serviced_subscription(ingestor): + """Creates a "full" interfaces.ServicedSubscription object. + + Args: + ingestor: An interfaces.ServicedIngestor. + + Returns: + An interfaces.ServicedSubscription object indicating a full + subscription. + """ + return _ServicedSubscription( + interfaces.ServicedSubscription.Kind.FULL, ingestor) + + +def wait_for_idle(end): + """Waits for an interfaces.End to complete all operations. + + Args: + end: Any interfaces.End. + """ + event = threading.Event() + end.add_idle_action(event.set) + event.wait() diff --git a/src/python/grpcio/grpc/framework/common/__init__.py b/src/python/grpcio/grpc/framework/common/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/common/cardinality.py b/src/python/grpcio/grpc/framework/common/cardinality.py new file mode 100644 index 0000000000..610425e803 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/cardinality.py @@ -0,0 +1,42 @@ +# 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. + +"""Defines an enum for classifying RPC methods by streaming semantics.""" + +import enum + + +@enum.unique +class Cardinality(enum.Enum): + """Describes the streaming semantics of an RPC method.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' diff --git a/src/python/grpcio/grpc/framework/common/style.py b/src/python/grpcio/grpc/framework/common/style.py new file mode 100644 index 0000000000..6ae694bdcb --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/style.py @@ -0,0 +1,40 @@ +# 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. + +"""Defines an enum for classifying RPC methods by control flow semantics.""" + +import enum + + +@enum.unique +class Service(enum.Enum): + """Describes the control flow style of RPC method implementation.""" + + INLINE = 'inline' + EVENT = 'event' diff --git a/src/python/grpcio/grpc/framework/common/test_constants.py b/src/python/grpcio/grpc/framework/common/test_constants.py new file mode 100644 index 0000000000..3126d0d82c --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/test_constants.py @@ -0,0 +1,43 @@ +# 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. + +"""Constants shared among tests throughout RPC Framework.""" + +# Value for maximum duration in seconds of RPCs that may time out as part of a +# test. +SHORT_TIMEOUT = 4 +# Absurdly large value for maximum duration in seconds for should-not-time-out +# RPCs made during tests. +LONG_TIMEOUT = 3000 + +# The number of payloads to transmit in streaming tests. +STREAM_LENGTH = 200 + +# The size of thread pools to use in tests. +POOL_SIZE = 10 diff --git a/src/python/grpcio/grpc/framework/common/test_control.py b/src/python/grpcio/grpc/framework/common/test_control.py new file mode 100644 index 0000000000..3960c4e649 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/test_control.py @@ -0,0 +1,87 @@ +# 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio/grpc/framework/common/test_coverage.py b/src/python/grpcio/grpc/framework/common/test_coverage.py new file mode 100644 index 0000000000..a7ed3582c4 --- /dev/null +++ b/src/python/grpcio/grpc/framework/common/test_coverage.py @@ -0,0 +1,116 @@ +# 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. + +"""Governs coverage for tests of RPCs throughout RPC Framework.""" + +import abc + +# This code is designed for use with the unittest module. +# pylint: disable=invalid-name + + +class Coverage(object): + """Specification of test coverage.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/__init__.py b/src/python/grpcio/grpc/framework/face/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/face/_calls.py b/src/python/grpcio/grpc/framework/face/_calls.py new file mode 100644 index 0000000000..87edeb0f0e --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_calls.py @@ -0,0 +1,422 @@ +# 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. + +"""Utility functions for invoking RPCs.""" + +import sys +import threading + +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.base import util as base_util +from grpc.framework.face import _control +from grpc.framework.face import interfaces +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import future + +_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' +_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' + + +class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, rendezvous): + self._rendezvous = rendezvous + + def consumer(self, operation_context): + return self._rendezvous + + +class _EventServicedIngestor(base_interfaces.ServicedIngestor): + + def __init__(self, result_consumer, abortion_callback): + self._result_consumer = result_consumer + self._abortion_callback = abortion_callback + + def consumer(self, operation_context): + operation_context.add_termination_callback( + _control.as_operation_termination_callback(self._abortion_callback)) + return self._result_consumer + + +def _rendezvous_subscription(rendezvous): + return base_util.full_serviced_subscription( + _RendezvousServicedIngestor(rendezvous)) + + +def _unary_event_subscription(completion_callback, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor( + _control.UnaryConsumer(completion_callback), abortion_callback)) + + +def _stream_event_subscription(result_consumer, abortion_callback): + return base_util.full_serviced_subscription( + _EventServicedIngestor(result_consumer, abortion_callback)) + + +# NOTE(nathaniel): This class has some extremely special semantics around +# cancellation that allow it to be used by both "blocking" APIs and "futures" +# APIs. +# +# Since futures.Future defines its own exception for cancellation, we want these +# objects, when returned by methods of a returning-Futures-from-other-methods +# object, to raise the same exception for cancellation. But that's weird in a +# blocking API - why should this object, also returned by methods of blocking +# APIs, raise exceptions from the "future" module? Should we do something like +# have this class be parameterized by the type of exception that it raises in +# cancellation circumstances? +# +# We don't have to take such a dramatic step: since blocking APIs define no +# cancellation semantics whatsoever, there is no supported way for +# blocking-API-users of these objects to cancel RPCs, and thus no supported way +# for them to see an exception the type of which would be weird to them. +# +# Bonus: in both blocking and futures APIs, this object still properly raises +# exceptions.CancellationError for any *server-side cancellation* of an RPC. +class _OperationCancellableIterator(interfaces.CancellableIterator): + """An interfaces.CancellableIterator for response-streaming operations.""" + + def __init__(self, rendezvous, operation): + self._lock = threading.Lock() + self._rendezvous = rendezvous + self._operation = operation + self._cancelled = False + + def __iter__(self): + return self + + def next(self): + with self._lock: + if self._cancelled: + raise future.CancelledError() + return next(self._rendezvous) + + def cancel(self): + with self._lock: + self._cancelled = True + self._operation.cancel() + self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED) + + +class _OperationFuture(future.Future): + """A future.Future interface to an operation.""" + + def __init__(self, rendezvous, operation): + self._condition = threading.Condition() + self._rendezvous = rendezvous + self._operation = operation + + self._cancelled = False + self._computed = False + self._payload = None + self._exception = None + self._traceback = None + self._callbacks = [] + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._condition: + if not self._cancelled and not self._computed: + self._operation.cancel() + self._cancelled = True + self._condition.notify_all() + return False + + def cancelled(self): + """See future.Future.cancelled for specification.""" + with self._condition: + return self._cancelled + + def running(self): + """See future.Future.running for specification.""" + with self._condition: + return not self._cancelled and not self._computed + + def done(self): + """See future.Future.done for specification.""" + with self._condition: + return self._cancelled or self._computed + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() + + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._traceback + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() + + def add_done_callback(self, fn): + """See future.Future.add_done_callback for specification.""" + with self._condition: + if self._callbacks is not None: + self._callbacks.append(fn) + return + + callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self) + + def on_operation_termination(self, operation_outcome): + """Indicates to this object that the operation has terminated. + + Args: + operation_outcome: A base_interfaces.Outcome value indicating the + outcome of the operation. + """ + with self._condition: + cancelled = self._cancelled + if cancelled: + callbacks = list(self._callbacks) + self._callbacks = None + else: + rendezvous = self._rendezvous + + if not cancelled: + payload = None + exception = None + traceback = None + if operation_outcome == base_interfaces.Outcome.COMPLETED: + try: + payload = next(rendezvous) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] + else: + try: + # We raise and then immediately catch in order to create a traceback. + raise _control.abortion_outcome_to_exception(operation_outcome) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] + with self._condition: + if not self._cancelled: + self._computed = True + self._payload = payload + self._exception = exception + self._traceback = traceback + callbacks = list(self._callbacks) + self._callbacks = None + + for callback in callbacks: + callable_util.call_logging_exceptions( + callback, _DONE_CALLBACK_LOG_MESSAGE, self) + + +class _Call(interfaces.Call): + + def __init__(self, operation): + self._operation = operation + self.context = _control.RpcContext(operation.context) + + def cancel(self): + self._operation.cancel() + + +def blocking_value_in_value_out(front, name, payload, timeout, trace_id): + """Services in a blocking fashion a value-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return next(rendezvous) + + +def future_value_in_value_out(front, name, payload, timeout, trace_id): + """Services a value-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_value_in_stream_out(front, name, payload, timeout, trace_id): + """Services a value-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + return _OperationCancellableIterator(rendezvous, operation) + + +def blocking_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id): + """Services in a blocking fashion a stream-in value-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + for payload in payload_iterator: + operation.consumer.consume(payload) + operation.consumer.terminate() + return next(rendezvous) + + +def future_stream_in_value_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in value-out servicer method by returning a Future.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + operation_future = _OperationFuture(rendezvous, operation) + operation.context.add_termination_callback( + operation_future.on_operation_termination) + return operation_future + + +def inline_stream_in_stream_out( + front, name, payload_iterator, timeout, trace_id, pool): + """Services a stream-in stream-out servicer method.""" + rendezvous = _control.Rendezvous() + subscription = _rendezvous_subscription(rendezvous) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + operation.context.add_termination_callback(rendezvous.set_outcome) + pool.submit( + callable_util.with_exceptions_logged( + _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), + payload_iterator, operation.consumer, lambda: True, True) + return _OperationCancellableIterator(rendezvous, operation) + + +def event_value_in_value_out( + front, name, payload, completion_callback, abortion_callback, timeout, + trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_value_in_stream_out( + front, name, payload, result_payload_consumer, abortion_callback, timeout, + trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate( + name, payload, True, timeout, subscription, trace_id) + return _Call(operation) + + +def event_stream_in_value_out( + front, name, completion_callback, abortion_callback, timeout, trace_id): + subscription = _unary_event_subscription( + completion_callback, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer + + +def event_stream_in_stream_out( + front, name, result_payload_consumer, abortion_callback, timeout, trace_id): + subscription = _stream_event_subscription( + result_payload_consumer, abortion_callback) + operation = front.operate(name, None, False, timeout, subscription, trace_id) + return _Call(operation), operation.consumer diff --git a/src/python/grpcio/grpc/framework/face/_control.py b/src/python/grpcio/grpc/framework/face/_control.py new file mode 100644 index 0000000000..e918907b74 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_control.py @@ -0,0 +1,198 @@ +# 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. + +"""State and behavior for translating between sync and async control flow.""" + +import threading + +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream + +INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' + +_OPERATION_OUTCOME_TO_RPC_ABORTION = { + base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED, + base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED, + base_interfaces.Outcome.RECEPTION_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + base_interfaces.Outcome.TRANSMISSION_FAILURE: + interfaces.Abortion.NETWORK_FAILURE, + base_interfaces.Outcome.SERVICED_FAILURE: + interfaces.Abortion.SERVICED_FAILURE, + base_interfaces.Outcome.SERVICER_FAILURE: + interfaces.Abortion.SERVICER_FAILURE, +} + + +def _as_operation_termination_callback(rpc_abortion_callback): + def operation_termination_callback(operation_outcome): + rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get( + operation_outcome, None) + if rpc_abortion is not None: + rpc_abortion_callback(rpc_abortion) + return operation_termination_callback + + +def _abortion_outcome_to_exception(abortion_outcome): + if abortion_outcome == base_interfaces.Outcome.CANCELLED: + return exceptions.CancellationError() + elif abortion_outcome == base_interfaces.Outcome.EXPIRED: + return exceptions.ExpirationError() + elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE: + return exceptions.ServicerError() + elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE: + return exceptions.ServicedError() + else: + return exceptions.NetworkError() + + +class UnaryConsumer(stream.Consumer): + """A stream.Consumer that should only ever be passed one value.""" + + def __init__(self, on_termination): + self._on_termination = on_termination + self._value = None + + def consume(self, value): + self._value = value + + def terminate(self): + self._on_termination(self._value) + + def consume_and_terminate(self, value): + self._on_termination(value) + + +class Rendezvous(stream.Consumer): + """A rendez-vous with stream.Consumer and iterator interfaces.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._values_completed = False + self._abortion = None + + def consume(self, value): + with self._condition: + self._values.append(value) + self._condition.notify() + + def terminate(self): + with self._condition: + self._values_completed = True + self._condition.notify() + + def consume_and_terminate(self, value): + with self._condition: + self._values.append(value) + self._values_completed = True + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while ((self._abortion is None) and + (not self._values) and + (not self._values_completed)): + self._condition.wait() + if self._abortion is not None: + raise _abortion_outcome_to_exception(self._abortion) + elif self._values: + return self._values.pop(0) + elif self._values_completed: + raise StopIteration() + else: + raise AssertionError('Unreachable code reached!') + + def set_outcome(self, outcome): + with self._condition: + if outcome is not base_interfaces.Outcome.COMPLETED: + self._abortion = outcome + self._condition.notify() + + +class RpcContext(interfaces.RpcContext): + """A wrapped base_interfaces.OperationContext.""" + + def __init__(self, operation_context): + self._operation_context = operation_context + + def is_active(self): + return self._operation_context.is_active() + + def time_remaining(self): + return self._operation_context.time_remaining() + + def add_abortion_callback(self, abortion_callback): + self._operation_context.add_termination_callback( + _as_operation_termination_callback(abortion_callback)) + + +def pipe_iterator_to_consumer(iterator, consumer, active, terminate): + """Pipes values emitted from an iterator to a stream.Consumer. + + Args: + iterator: An iterator from which values will be emitted. + consumer: A stream.Consumer to which values will be passed. + active: A no-argument callable that returns True if the work being done by + this function is still valid and should not be abandoned and False if the + work being done by this function should be abandoned. + terminate: A boolean indicating whether or not this function should + terminate the given consumer after passing to it all values emitted by the + given iterator. + + Raises: + abandonment.Abandoned: If this function quits early after seeing False + returned by the active function passed to it. + Exception: This function raises whatever exceptions are raised by iterating + over the given iterator. + """ + for element in iterator: + if not active(): + raise abandonment.Abandoned() + + consumer.consume(element) + + if not active(): + raise abandonment.Abandoned() + if terminate: + consumer.terminate() + + +def abortion_outcome_to_exception(abortion_outcome): + return _abortion_outcome_to_exception(abortion_outcome) + + +def as_operation_termination_callback(rpc_abortion_callback): + return _as_operation_termination_callback(rpc_abortion_callback) diff --git a/src/python/grpcio/grpc/framework/face/_service.py b/src/python/grpcio/grpc/framework/face/_service.py new file mode 100644 index 0000000000..cdf413356a --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_service.py @@ -0,0 +1,187 @@ +# 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. + +"""Behaviors for servicing RPCs.""" + +# base_interfaces and interfaces are referenced from specification in this +# module. +from grpc.framework.base import interfaces as base_interfaces # pylint: disable=unused-import +from grpc.framework.face import _control +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import callable_util +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util + + +class _ValueInStreamOutConsumer(stream.Consumer): + """A stream.Consumer that maps inputs one-to-many onto outputs.""" + + def __init__(self, behavior, context, downstream): + """Constructor. + + Args: + behavior: A callable that takes a single value and an + interfaces.RpcContext and returns a generator of arbitrarily many + values. + context: An interfaces.RpcContext. + downstream: A stream.Consumer to which to pass the values generated by the + given behavior. + """ + self._behavior = behavior + self._context = context + self._downstream = downstream + + def consume(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, False) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + _control.pipe_iterator_to_consumer( + self._behavior(value, self._context), self._downstream, + self._context.is_active, True) + + +def _pool_wrap(behavior, operation_context): + """Wraps an operation-related behavior so that it may be called in a pool. + + Args: + behavior: A callable related to carrying out an operation. + operation_context: A base_interfaces.OperationContext for the operation. + + Returns: + A callable that when called carries out the behavior of the given callable + and handles whatever exceptions it raises appropriately. + """ + def translation(*args): + try: + behavior(*args) + except ( + abandonment.Abandoned, + exceptions.ExpirationError, + exceptions.CancellationError, + exceptions.ServicedError, + exceptions.NetworkError) as e: + if operation_context.is_active(): + operation_context.fail(e) + except Exception as e: + operation_context.fail(e) + return callable_util.with_exceptions_logged( + translation, _control.INTERNAL_ERROR_LOG_MESSAGE) + + +def adapt_inline_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return stream_util.TransformingConsumer( + lambda request: method(request, rpc_context), response_consumer) + return adaptation + + +def adapt_inline_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return _ValueInStreamOutConsumer(method, rpc_context, response_consumer) + return adaptation + + +def adapt_inline_stream_in_value_out(method, pool): + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + response_consumer.consume_and_terminate( + method(rendezvous, _control.RpcContext(operation_context))) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_inline_stream_in_stream_out(method, pool): + """Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers. + + RPCs may be serviced by calling the return value of this function, passing + request values to the stream.Consumer returned from that call, and receiving + response values from the stream.Consumer passed to that call. + + Args: + method: An interfaces.InlineStreamInStreamOutMethod. + pool: A thread pool. + + Returns: + A callable that takes a stream.Consumer and a + base_interfaces.OperationContext and returns a stream.Consumer. + """ + def adaptation(response_consumer, operation_context): + rendezvous = _control.Rendezvous() + operation_context.add_termination_callback(rendezvous.set_outcome) + def in_pool_thread(): + _control.pipe_iterator_to_consumer( + method(rendezvous, _control.RpcContext(operation_context)), + response_consumer, operation_context.is_active, True) + pool.submit(_pool_wrap(in_pool_thread, operation_context)) + return rendezvous + return adaptation + + +def adapt_event_value_in_value_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method( + payload, response_consumer.consume_and_terminate, + _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_value_in_stream_out(method): + def adaptation(response_consumer, operation_context): + def on_payload(payload): + method( + payload, response_consumer, _control.RpcContext(operation_context)) + return _control.UnaryConsumer(on_payload) + return adaptation + + +def adapt_event_stream_in_value_out(method): + def adaptation(response_consumer, operation_context): + rpc_context = _control.RpcContext(operation_context) + return method(response_consumer.consume_and_terminate, rpc_context) + return adaptation + + +def adapt_event_stream_in_stream_out(method): + def adaptation(response_consumer, operation_context): + return method(response_consumer, _control.RpcContext(operation_context)) + return adaptation diff --git a/src/python/grpcio/grpc/framework/face/_test_case.py b/src/python/grpcio/grpc/framework/face/_test_case.py new file mode 100644 index 0000000000..642d500628 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/_test_case.py @@ -0,0 +1,61 @@ +# 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. + +"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" + +from grpc.framework.face import implementations +from grpc.framework.face.testing import base_util +from grpc.framework.face.testing import test_case +from grpc.framework.foundation import logging_pool + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 10 + + +class FaceTestCase(test_case.FaceTestCase): + """Provides abstract Face-layer tests an in-memory implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = implementations.servicer( + servicer_pool, method_implementations, multi_method_implementation) + + linked_pair = base_util.linked_pair(servicer, _TIMEOUT) + stub = implementations.generic_stub(linked_pair.front, stub_pool) + return stub, (servicer_pool, stub_pool, linked_pair) + + def tear_down_implementation(self, memo): + servicer_pool, stub_pool, linked_pair = memo + linked_pair.shut_down() + stub_pool.shutdown(wait=True) + servicer_pool.shutdown(wait=True) diff --git a/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py b/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py new file mode 100644 index 0000000000..763f0f0edc --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc.framework.face import _test_case +from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/demonstration.py b/src/python/grpcio/grpc/framework/face/demonstration.py new file mode 100644 index 0000000000..f6b4b609ff --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/demonstration.py @@ -0,0 +1,118 @@ +# 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. + +"""Demonstration-suitable implementation of the face layer of RPC Framework.""" + +from grpc.framework.base import util as _base_util +from grpc.framework.base import implementations as _base_implementations +from grpc.framework.face import implementations +from grpc.framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 5 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Server and Stub that are linked to one another. + + Attributes: + server: A Server. + stub: A Stub. + """ + + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, server, stub, front, back, pools): + self.server = server + self.stub = stub + self._front = front + self._back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self._front) + _base_util.wait_for_idle(self._back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def server_and_stub( + default_timeout, + inline_value_in_value_out_methods=None, + inline_value_in_stream_out_methods=None, + inline_stream_in_value_out_methods=None, + inline_stream_in_stream_out_methods=None, + event_value_in_value_out_methods=None, + event_value_in_stream_out_methods=None, + event_stream_in_value_out_methods=None, + event_stream_in_stream_out_methods=None, + multi_method=None): + """Creates a Server and Stub linked together for use.""" + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool, + stub_pool) + + servicer = implementations.servicer( + back_work_pool, + inline_value_in_value_out_methods=inline_value_in_value_out_methods, + inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, + inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, + inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, + event_value_in_value_out_methods=event_value_in_value_out_methods, + event_value_in_stream_out_methods=event_value_in_stream_out_methods, + event_stream_in_value_out_methods=event_stream_in_value_out_methods, + event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, + multi_method=multi_method) + + front = _base_implementations.front_link( + front_work_pool, front_transmission_pool, front_utility_pool) + back = _base_implementations.back_link( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(back) + back.join_fore_link(front) + + stub = implementations.stub(front, stub_pool) + + return _LinkedPair(implementations.server(), stub, front, back, pools) diff --git a/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py new file mode 100644 index 0000000000..e1ab3cf711 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc.framework.face import _test_case +from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/exceptions.py b/src/python/grpcio/grpc/framework/face/exceptions.py new file mode 100644 index 0000000000..f112df70bc --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/exceptions.py @@ -0,0 +1,77 @@ +# 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. + +"""Exceptions used in the Face layer of RPC Framework.""" + +import abc + + +class NoSuchMethodError(Exception): + """Raised by customer code to indicate an unrecognized RPC method name. + + Attributes: + name: The unrecognized name. + """ + + def __init__(self, name): + """Constructor. + + Args: + name: The unrecognized RPC method name. + """ + super(NoSuchMethodError, self).__init__() + self.name = name + + +class RpcError(Exception): + """Common super type for all exceptions raised by the Face layer. + + Only RPC Framework should instantiate and raise these exceptions. + """ + __metaclass__ = abc.ABCMeta + + +class CancellationError(RpcError): + """Indicates that an RPC has been cancelled.""" + + +class ExpirationError(RpcError): + """Indicates that an RPC has expired ("timed out").""" + + +class NetworkError(RpcError): + """Indicates that some error occurred on the network.""" + + +class ServicedError(RpcError): + """Indicates that the Serviced failed in the course of an RPC.""" + + +class ServicerError(RpcError): + """Indicates that the Servicer failed in the course of servicing an RPC.""" diff --git a/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py new file mode 100644 index 0000000000..2d13bb911d --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc.framework.face import _test_case +from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/implementations.py b/src/python/grpcio/grpc/framework/face/implementations.py new file mode 100644 index 0000000000..4a6de52974 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/implementations.py @@ -0,0 +1,318 @@ +# 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. + +"""Entry points into the Face layer of RPC Framework.""" + +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.base import exceptions as _base_exceptions +from grpc.framework.base import interfaces as base_interfaces +from grpc.framework.face import _calls +from grpc.framework.face import _service +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces + + +class _BaseServicer(base_interfaces.Servicer): + + def __init__(self, methods, multi_method): + self._methods = methods + self._multi_method = multi_method + + def service(self, name, context, output_consumer): + method = self._methods.get(name, None) + if method is not None: + return method(output_consumer, context) + elif self._multi_method is not None: + try: + return self._multi_method.service(name, output_consumer, context) + except exceptions.NoSuchMethodError: + raise _base_exceptions.NoSuchMethodError() + else: + raise _base_exceptions.NoSuchMethodError() + + +class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable): + + def __init__(self, front, name): + self._front = front + self._name = name + + def __call__(self, request, timeout): + return _calls.blocking_value_in_value_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def future(self, request, timeout): + return _calls.future_value_in_value_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def event(self, request, response_callback, abortion_callback, timeout): + return _calls.event_value_in_value_out( + self._front, self._name, request, response_callback, abortion_callback, + timeout, 'unused trace ID') + + +class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable): + + def __init__(self, front, name): + self._front = front + self._name = name + + def __call__(self, request, timeout): + return _calls.inline_value_in_stream_out( + self._front, self._name, request, timeout, 'unused trace ID') + + def event(self, request, response_consumer, abortion_callback, timeout): + return _calls.event_value_in_stream_out( + self._front, self._name, request, response_consumer, abortion_callback, + timeout, 'unused trace ID') + + +class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable): + + def __init__(self, front, name, pool): + self._front = front + self._name = name + self._pool = pool + + def __call__(self, request_iterator, timeout): + return _calls.blocking_stream_in_value_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID') + + def future(self, request_iterator, timeout): + return _calls.future_stream_in_value_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event(self, response_callback, abortion_callback, timeout): + return _calls.event_stream_in_value_out( + self._front, self._name, response_callback, abortion_callback, timeout, + 'unused trace ID') + + +class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable): + + def __init__(self, front, name, pool): + self._front = front + self._name = name + self._pool = pool + + def __call__(self, request_iterator, timeout): + return _calls.inline_stream_in_stream_out( + self._front, self._name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event(self, response_consumer, abortion_callback, timeout): + return _calls.event_stream_in_stream_out( + self._front, self._name, response_consumer, abortion_callback, timeout, + 'unused trace ID') + + +class _GenericStub(interfaces.GenericStub): + """An interfaces.GenericStub implementation.""" + + def __init__(self, front, pool): + self._front = front + self._pool = pool + + def blocking_value_in_value_out(self, name, request, timeout): + return _calls.blocking_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def future_value_in_value_out(self, name, request, timeout): + return _calls.future_value_in_value_out( + self._front, name, request, timeout, 'unused trace ID') + + def inline_value_in_stream_out(self, name, request, timeout): + return _calls.inline_value_in_stream_out( + self._front, name, request, timeout, 'unused trace ID') + + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.blocking_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID') + + def future_stream_in_value_out(self, name, request_iterator, timeout): + return _calls.future_stream_in_value_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + return _calls.inline_stream_in_stream_out( + self._front, name, request_iterator, timeout, 'unused trace ID', + self._pool) + + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + return _calls.event_value_in_value_out( + self._front, name, request, response_callback, abortion_callback, + timeout, 'unused trace ID') + + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + return _calls.event_value_in_stream_out( + self._front, name, request, response_consumer, abortion_callback, + timeout, 'unused trace ID') + + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + return _calls.event_stream_in_value_out( + self._front, name, response_callback, abortion_callback, timeout, + 'unused trace ID') + + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + return _calls.event_stream_in_stream_out( + self._front, name, response_consumer, abortion_callback, timeout, + 'unused trace ID') + + def unary_unary_multi_callable(self, name): + return _UnaryUnaryMultiCallable(self._front, name) + + def unary_stream_multi_callable(self, name): + return _UnaryStreamMultiCallable(self._front, name) + + def stream_unary_multi_callable(self, name): + return _StreamUnaryMultiCallable(self._front, name, self._pool) + + def stream_stream_multi_callable(self, name): + return _StreamStreamMultiCallable(self._front, name, self._pool) + + +class _DynamicStub(interfaces.DynamicStub): + """An interfaces.DynamicStub implementation.""" + + def __init__(self, cardinalities, front, pool): + self._cardinalities = cardinalities + self._front = front + self._pool = pool + + def __getattr__(self, attr): + method_cardinality = self._cardinalities.get(attr) + if method_cardinality is cardinality.Cardinality.UNARY_UNARY: + return _UnaryUnaryMultiCallable(self._front, attr) + elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: + return _UnaryStreamMultiCallable(self._front, attr) + elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: + return _StreamUnaryMultiCallable(self._front, attr, self._pool) + elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: + return _StreamStreamMultiCallable(self._front, attr, self._pool) + else: + raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr) + + +def _adapt_method_implementations(method_implementations, pool): + adapted_implementations = {} + for name, method_implementation in method_implementations.iteritems(): + if method_implementation.style is style.Service.INLINE: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_inline_value_in_value_out( + method_implementation.unary_unary_inline) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_inline_value_in_stream_out( + method_implementation.unary_stream_inline) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_inline_stream_in_value_out( + method_implementation.stream_unary_inline, pool) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out( + method_implementation.stream_stream_inline, pool) + elif method_implementation.style is style.Service.EVENT: + if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: + adapted_implementations[name] = _service.adapt_event_value_in_value_out( + method_implementation.unary_unary_event) + elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: + adapted_implementations[name] = _service.adapt_event_value_in_stream_out( + method_implementation.unary_stream_event) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: + adapted_implementations[name] = _service.adapt_event_stream_in_value_out( + method_implementation.stream_unary_event) + elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: + adapted_implementations[name] = _service.adapt_event_stream_in_stream_out( + method_implementation.stream_stream_event) + return adapted_implementations + + +def servicer(pool, method_implementations, multi_method_implementation): + """Creates a base_interfaces.Servicer. + + It is guaranteed that any passed interfaces.MultiMethodImplementation will + only be called to service an RPC if there is no + interfaces.MethodImplementation for the RPC method in the passed + method_implementations dictionary. + + Args: + pool: A thread pool. + method_implementations: A dictionary from RPC method name to + interfaces.MethodImplementation object to be used to service the named + RPC method. + multi_method_implementation: An interfaces.MultiMethodImplementation to be + used to service any RPCs not serviced by the + interfaces.MethodImplementations given in the method_implementations + dictionary, or None. + + Returns: + A base_interfaces.Servicer that services RPCs via the given implementations. + """ + adapted_implementations = _adapt_method_implementations( + method_implementations, pool) + return _BaseServicer(adapted_implementations, multi_method_implementation) + + +def generic_stub(front, pool): + """Creates an interfaces.GenericStub. + + Args: + front: A base_interfaces.Front. + pool: A futures.ThreadPoolExecutor. + + Returns: + An interfaces.GenericStub that performs RPCs via the given + base_interfaces.Front. + """ + return _GenericStub(front, pool) + + +def dynamic_stub(cardinalities, front, pool, prefix): + """Creates an interfaces.DynamicStub. + + Args: + cardinalities: A dict from RPC method name to cardinality.Cardinality + value identifying the cardinality of every RPC method to be supported by + the created interfaces.DynamicStub. + front: A base_interfaces.Front. + pool: A futures.ThreadPoolExecutor. + prefix: A string to prepend when mapping requested attribute name to RPC + method name during attribute access on the created + interfaces.DynamicStub. + + Returns: + An interfaces.DynamicStub that performs RPCs via the given + base_interfaces.Front. + """ + return _DynamicStub(cardinalities, front, pool) diff --git a/src/python/grpcio/grpc/framework/face/interfaces.py b/src/python/grpcio/grpc/framework/face/interfaces.py new file mode 100644 index 0000000000..b7cc4c1169 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/interfaces.py @@ -0,0 +1,640 @@ +# 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. + +"""Interfaces for the face layer of RPC Framework.""" + +import abc +import enum + +# cardinality, style, exceptions, abandonment, future, and stream are +# referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import +from grpc.framework.common import style # pylint: disable=unused-import +from grpc.framework.face import exceptions # pylint: disable=unused-import +from grpc.framework.foundation import abandonment # pylint: disable=unused-import +from grpc.framework.foundation import future # pylint: disable=unused-import +from grpc.framework.foundation import stream # pylint: disable=unused-import + + +@enum.unique +class Abortion(enum.Enum): + """Categories of RPC abortion.""" + CANCELLED = 'cancelled' + EXPIRED = 'expired' + NETWORK_FAILURE = 'network failure' + SERVICED_FAILURE = 'serviced failure' + SERVICER_FAILURE = 'servicer failure' + + +class CancellableIterator(object): + """Implements the Iterator protocol and affords a cancel method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __iter__(self): + """Returns the self object in accordance with the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def next(self): + """Returns a value or raises StopIteration per the Iterator protocol.""" + raise NotImplementedError() + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of whatever computation underlies this iterator.""" + raise NotImplementedError() + + +class RpcContext(object): + """Provides RPC-related information and action.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def is_active(self): + """Describes whether the RPC is active or has terminated.""" + raise NotImplementedError() + + @abc.abstractmethod + def time_remaining(self): + """Describes the length of allowed time remaining for the RPC. + + Returns: + A nonnegative float indicating the length of allowed time in seconds + remaining for the RPC to complete before it is considered to have timed + out. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_abortion_callback(self, abortion_callback): + """Registers a callback to be called if the RPC is aborted. + + Args: + abortion_callback: A callable to be called and passed an Abortion value + in the event of RPC abortion. + """ + raise NotImplementedError() + + +class Call(object): + """Invocation-side representation of an RPC. + + Attributes: + context: An RpcContext affording information about the RPC. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cancel(self): + """Requests cancellation of the RPC.""" + raise NotImplementedError() + + +class UnaryUnaryMultiCallable(object): + """Affords invoking a unary-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future(self, request, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, request, response_callback, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_callback: A callback to be called to accept the restponse value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + +class UnaryStreamMultiCallable(object): + """Affords invoking a unary-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion + of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, request, response_consumer, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the restponse + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + +class StreamUnaryMultiCallable(object): + """Affords invoking a stream-unary RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future(self, request_iterator, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future's result value will be the response value of the RPC. + In the event of RPC abortion, the returned Future's exception value + will be an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, response_callback, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + + Args: + request: The request value for the RPC. + response_callback: A callback to be called to accept the restponse value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class StreamStreamMultiCallable(object): + """Affords invoking a stream-stream RPC in any call style.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __call__(self, request_iterator, timeout): + """Synchronously invokes the underlying RPC. + + Args: + request_iterator: An iterator that yields request values for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion + of the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event(self, response_consumer, abortion_callback, timeout): + """Asynchronously invokes the underlying RPC. + +l Args: + response_consumer: A stream.Consumer to be called to accept the restponse + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + +class MethodImplementation(object): + """A sum type that describes an RPC method implementation. + + Attributes: + cardinality: A cardinality.Cardinality value. + style: A style.Service value. + unary_unary_inline: The implementation of the RPC method as a callable + value that takes a request value and an RpcContext object and returns a + response value. Only non-None if cardinality is + cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE. + unary_stream_inline: The implementation of the RPC method as a callable + value that takes a request value and an RpcContext object and returns an + iterator of response values. Only non-None if cardinality is + cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE. + stream_unary_inline: The implementation of the RPC method as a callable + value that takes an iterator of request values and an RpcContext object + and returns a response value. Only non-None if cardinality is + cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE. + stream_stream_inline: The implementation of the RPC method as a callable + value that takes an iterator of request values and an RpcContext object + and returns an iterator of response values. Only non-None if cardinality + is cardinality.Cardinality.STREAM_STREAM and style is + style.Service.INLINE. + unary_unary_event: The implementation of the RPC method as a callable value + that takes a request value, a response callback to which to pass the + response value of the RPC, and an RpcContext. Only non-None if + cardinality is cardinality.Cardinality.UNARY_UNARY and style is + style.Service.EVENT. + unary_stream_event: The implementation of the RPC method as a callable + value that takes a request value, a stream.Consumer to which to pass the + the response values of the RPC, and an RpcContext. Only non-None if + cardinality is cardinality.Cardinality.UNARY_STREAM and style is + style.Service.EVENT. + stream_unary_event: The implementation of the RPC method as a callable + value that takes a response callback to which to pass the response value + of the RPC and an RpcContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT. + stream_stream_event: The implementation of the RPC method as a callable + value that takes a stream.Consumer to which to pass the response values + of the RPC and an RpcContext and returns a stream.Consumer to which the + request values of the RPC should be passed. Only non-None if cardinality + is cardinality.Cardinality.STREAM_STREAM and style is + style.Service.EVENT. + """ + __metaclass__ = abc.ABCMeta + + +class MultiMethodImplementation(object): + """A general type able to service many RPC methods.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, name, response_consumer, context): + """Services an RPC. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + context: An RpcContext object. + + Returns: + A stream.Consumer with which to accept the request values of the RPC. The + consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing values to this object. Implementations must not assume that this + object will be called to completion of the request stream or even called + at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + exceptions.NoSuchMethodError: If this MultiMethod does not recognize the + given RPC method name and is not able to service the RPC. + """ + raise NotImplementedError() + + +class GenericStub(object): + """Affords RPC methods to callers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def blocking_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_value_in_value_out(self, name, request, timeout): + """Invokes a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_value_in_stream_out(self, name, request, timeout): + """Invokes a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def blocking_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + This method blocks until either returning the response value of the RPC + (in the event of RPC completion) or raising an exception (in the event of + RPC abortion). + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + The response value for the RPC. + + Raises: + exceptions.RpcError: Indicating that the RPC was aborted. + """ + raise NotImplementedError() + + @abc.abstractmethod + def future_stream_in_value_out(self, name, request_iterator, timeout): + """Invokes a stream-request-unary-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A future.Future representing the RPC. In the event of RPC completion, the + returned Future will return an outcome indicating that the RPC returned + the response value of the RPC. In the event of RPC abortion, the + returned Future will return an outcome indicating that the RPC raised + an exceptions.RpcError. + """ + raise NotImplementedError() + + @abc.abstractmethod + def inline_stream_in_stream_out(self, name, request_iterator, timeout): + """Invokes a stream-request-stream-response RPC method. + + Args: + name: The RPC method name. + request_iterator: An iterator that yields the request values of the RPC. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A CancellableIterator that yields the response values of the RPC and + affords RPC cancellation. Drawing response values from the returned + CancellableIterator may raise exceptions.RpcError indicating abortion of + the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_value_out( + self, name, request, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_value_in_stream_out( + self, name, request, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + request: The request value for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A Call object for the RPC. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_value_out( + self, name, response_callback, abortion_callback, timeout): + """Event-driven invocation of a unary-request-unary-response RPC method. + + Args: + name: The RPC method name. + response_callback: A callback to be called to accept the response value + of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def event_stream_in_stream_out( + self, name, response_consumer, abortion_callback, timeout): + """Event-driven invocation of a unary-request-stream-response RPC method. + + Args: + name: The RPC method name. + response_consumer: A stream.Consumer to be called to accept the response + values of the RPC. + abortion_callback: A callback to be called and passed an Abortion value + in the event of RPC abortion. + timeout: A duration of time in seconds to allow for the RPC. + + Returns: + A pair of a Call object for the RPC and a stream.Consumer to which the + request values of the RPC should be passed. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_multi_callable(self, name): + """Creates a UnaryUnaryMultiCallable for a unary-unary RPC method. + + Args: + name: The RPC method name. + + Returns: + A UnaryUnaryMultiCallable value for the named unary-unary RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_multi_callable(self, name): + """Creates a UnaryStreamMultiCallable for a unary-stream RPC method. + + Args: + name: The RPC method name. + + Returns: + A UnaryStreamMultiCallable value for the name unary-stream RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_multi_callable(self, name): + """Creates a StreamUnaryMultiCallable for a stream-unary RPC method. + + Args: + name: The RPC method name. + + Returns: + A StreamUnaryMultiCallable value for the named stream-unary RPC method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_multi_callable(self, name): + """Creates a StreamStreamMultiCallable for a stream-stream RPC method. + + Args: + name: The RPC method name. + + Returns: + A StreamStreamMultiCallable value for the named stream-stream RPC method. + """ + raise NotImplementedError() + + +class DynamicStub(object): + """A stub with RPC-method-bound multi-callable attributes. + + Instances of this type responsd to attribute access as follows: if the + requested attribute is the name of a unary-unary RPC method, the value of the + attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC + method; if the requested attribute is the name of a unary-stream RPC method, + the value of the attribute will be a UnaryStreamMultiCallable with which to + invoke the RPC method; if the requested attribute is the name of a + stream-unary RPC method, the value of the attribute will be a + StreamUnaryMultiCallable with which to invoke the RPC method; and if the + requested attribute is the name of a stream-stream RPC method, the value of + the attribute will be a StreamStreamMultiCallable with which to invoke the + RPC method. + """ + __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/face/testing/__init__.py b/src/python/grpcio/grpc/framework/face/testing/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/face/testing/base_util.py b/src/python/grpcio/grpc/framework/face/testing/base_util.py new file mode 100644 index 0000000000..1df1529b27 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/base_util.py @@ -0,0 +1,102 @@ +# 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. + +"""Utilities for creating Base-layer objects for use in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.base import util as _base_util +from grpc.framework.base import implementations +from grpc.framework.base import in_memory +from grpc.framework.base import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 5 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Front and Back that are linked to one another. + + Attributes: + front: An interfaces.Front. + back: An interfaces.Back. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, front, back, pools): + self.front = front + self.back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self.front) + _base_util.wait_for_idle(self.back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def linked_pair(servicer, default_timeout): + """Creates a Server and Stub linked together for use.""" + link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + link_pool, + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool) + + link = in_memory.Link(link_pool) + front = implementations.front_link( + front_work_pool, front_transmission_pool, front_utility_pool) + back = implementations.back_link( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(link) + link.join_fore_link(front) + back.join_fore_link(link) + link.join_rear_link(back) + + return _LinkedPair(front, back, pools) diff --git a/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py new file mode 100644 index 0000000000..e57ee00104 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py @@ -0,0 +1,222 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.face import exceptions +from grpc.framework.face.testing import control +from grpc.framework.face.testing import coverage +from grpc.framework.face.testing import digest +from grpc.framework.face.testing import stock_service +from grpc.framework.face.testing import test_case + +_TIMEOUT = 3 +_LONG_TIMEOUT = 45 + + +class BlockingInvocationInlineServiceTestCase( + test_case.FaceTestCase, coverage.BlockingCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.inline_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response = self.stub.blocking_value_in_value_out( + name, request, _LONG_TIMEOUT) + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response = self.stub.blocking_stream_in_value_out( + name, iter(requests), _LONG_TIMEOUT) + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response = self.stub.blocking_value_in_value_out( + name, first_request, _TIMEOUT) + + test_messages.verify(first_request, first_response, self) + + second_response = self.stub.blocking_value_in_value_out( + name, second_request, _TIMEOUT) + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.unary_unary_multi_callable(name) + multi_callable(request, _TIMEOUT) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.stream_unary_multi_callable(name) + multi_callable(iter(requests), _TIMEOUT) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) diff --git a/src/python/grpcio/grpc/framework/face/testing/callback.py b/src/python/grpcio/grpc/framework/face/testing/callback.py new file mode 100644 index 0000000000..d0e63c8c56 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/callback.py @@ -0,0 +1,94 @@ +# 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. + +"""A utility useful in tests of asynchronous, event-driven interfaces.""" + +import threading + +from grpc.framework.foundation import stream + + +class Callback(stream.Consumer): + """A utility object useful in tests of asynchronous code.""" + + def __init__(self): + self._condition = threading.Condition() + self._unary_response = None + self._streamed_responses = [] + self._completed = False + self._abortion = None + + def abort(self, abortion): + with self._condition: + self._abortion = abortion + self._condition.notify_all() + + def complete(self, unary_response): + with self._condition: + self._unary_response = unary_response + self._completed = True + self._condition.notify_all() + + def consume(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + + def terminate(self): + with self._condition: + self._completed = True + self._condition.notify_all() + + def consume_and_terminate(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + self._completed = True + self._condition.notify_all() + + def block_until_terminated(self): + with self._condition: + while self._abortion is None and not self._completed: + self._condition.wait() + + def response(self): + with self._condition: + if self._abortion is None: + return self._unary_response + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def responses(self): + with self._condition: + if self._abortion is None: + return list(self._streamed_responses) + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def abortion(self): + with self._condition: + return self._abortion diff --git a/src/python/grpcio/grpc/framework/face/testing/control.py b/src/python/grpcio/grpc/framework/face/testing/control.py new file mode 100644 index 0000000000..3960c4e649 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/control.py @@ -0,0 +1,87 @@ +# 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio/grpc/framework/face/testing/coverage.py b/src/python/grpcio/grpc/framework/face/testing/coverage.py new file mode 100644 index 0000000000..f3aca113fe --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/coverage.py @@ -0,0 +1,123 @@ +# 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. + +"""Governs coverage for the tests of the Face layer of RPC Framework.""" + +import abc + +# These classes are only valid when inherited by unittest.TestCases. +# pylint: disable=invalid-name + + +class BlockingCoverage(object): + """Specification of test coverage for blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() + + +class FullCoverage(BlockingCoverage): + """Specification of test coverage for non-blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/digest.py b/src/python/grpcio/grpc/framework/face/testing/digest.py new file mode 100644 index 0000000000..db8fcbb018 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/digest.py @@ -0,0 +1,450 @@ +# 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. + +"""Code for making a service.TestService more amenable to use in tests.""" + +import collections +import threading + +# testing_control, interfaces, and testing_service are referenced from +# specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces as face_interfaces +from grpc.framework.face.testing import control as testing_control # pylint: disable=unused-import +from grpc.framework.face.testing import interfaces # pylint: disable=unused-import +from grpc.framework.face.testing import service as testing_service # pylint: disable=unused-import +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util + +_IDENTITY = lambda x: x + + +class TestServiceDigest( + collections.namedtuple( + 'TestServiceDigest', + ['name', + 'methods', + 'inline_method_implementations', + 'event_method_implementations', + 'multi_method_implementation', + 'unary_unary_messages_sequences', + 'unary_stream_messages_sequences', + 'stream_unary_messages_sequences', + 'stream_stream_messages_sequences'])): + """A transformation of a service.TestService. + + Attributes: + name: The RPC service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + inline_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + in-line calls to behaviors under test. + event_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + event-driven calls to behaviors under test. + multi_method_implementation: A face_interfaces.MultiMethodImplementation to + be used in tests of generic calls to behaviors under test. + unary_unary_messages_sequences: A dict from method name to sequence of + service.UnaryUnaryTestMessages objects to be used to test the method + with the given name. + unary_stream_messages_sequences: A dict from method name to sequence of + service.UnaryStreamTestMessages objects to be used to test the method + with the given name. + stream_unary_messages_sequences: A dict from method name to sequence of + service.StreamUnaryTestMessages objects to be used to test the method + with the given name. + stream_stream_messages_sequences: A dict from method name to sequence of + service.StreamStreamTestMessages objects to be used to test the + method with the given name. + serialization: A serial.Serialization object describing serialization + behaviors for all the RPC methods. + """ + + +class _BufferingConsumer(stream.Consumer): + """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" + + def __init__(self): + self.consumed = [] + self.terminated = False + + def consume(self, value): + self.consumed.append(value) + + def terminate(self): + self.terminated = True + + def consume_and_terminate(self, value): + self.consumed.append(value) + self.terminated = True + + +class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control): + self._test_method = unary_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.INLINE + + def unary_unary_inline(self, request, context): + response_list = [] + self._test_method.service( + request, response_list.append, context, self._control) + return response_list.pop(0) + + +class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control, pool): + self._test_method = unary_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.EVENT + + def unary_unary_event(self, request, response_callback, context): + if self._pool is None: + self._test_method.service( + request, response_callback, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_callback, context, + self._control) + + +class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control): + self._test_method = unary_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.INLINE + + def unary_stream_inline(self, request, context): + response_consumer = _BufferingConsumer() + self._test_method.service( + request, response_consumer, context, self._control) + for response in response_consumer.consumed: + yield response + + +class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control, pool): + self._test_method = unary_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.EVENT + + def unary_stream_event(self, request, response_consumer, context): + if self._pool is None: + self._test_method.service( + request, response_consumer, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_consumer, context, + self._control) + + +class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control): + self._test_method = stream_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.INLINE + + def stream_unary_inline(self, request_iterator, context): + response_list = [] + request_consumer = self._test_method.service( + response_list.append, context, self._control) + for request in request_iterator: + request_consumer.consume(request) + request_consumer.terminate() + return response_list.pop(0) + + +class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control, pool): + self._test_method = stream_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.EVENT + + def stream_unary_event(self, response_callback, context): + request_consumer = self._test_method.service( + response_callback, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control): + self._test_method = stream_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.INLINE + + def stream_stream_inline(self, request_iterator, context): + response_consumer = _BufferingConsumer() + request_consumer = self._test_method.service( + response_consumer, context, self._control) + + for request in request_iterator: + request_consumer.consume(request) + while response_consumer.consumed: + yield response_consumer.consumed.pop(0) + response_consumer.terminate() + + +class _EventStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control, pool): + self._test_method = stream_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.EVENT + + def stream_stream_event(self, response_consumer, context): + request_consumer = self._test_method.service( + response_consumer, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _UnaryConsumer(stream.Consumer): + """A Consumer that only allows consumption of exactly one value.""" + + def __init__(self, action): + self._lock = threading.Lock() + self._action = action + self._consumed = False + self._terminated = False + + def consume(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + + self._action(value) + + def terminate(self): + with self._lock: + if not self._consumed: + raise ValueError('Unary consumer hasn\'t yet consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._terminated = True + + def consume_and_terminate(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + self._terminated = True + + self._action(value) + + +class _UnaryUnaryAdaptation(object): + + def __init__(self, unary_unary_test_method): + self._method = unary_unary_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service( + request, response_consumer.consume_and_terminate, context, control) + return _UnaryConsumer(action) + + +class _UnaryStreamAdaptation(object): + + def __init__(self, unary_stream_test_method): + self._method = unary_stream_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service(request, response_consumer, context, control) + return _UnaryConsumer(action) + + +class _StreamUnaryAdaptation(object): + + def __init__(self, stream_unary_test_method): + self._method = stream_unary_test_method + + def service(self, response_consumer, context, control): + return self._method.service( + response_consumer.consume_and_terminate, context, control) + + +class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): + + def __init__(self, methods, control, pool): + self._methods = methods + self._control = control + self._pool = pool + + def service(self, name, response_consumer, context): + method = self._methods.get(name, None) + if method is None: + raise exceptions.NoSuchMethodError(name) + elif self._pool is None: + return method(response_consumer, context, self._control) + else: + request_consumer = method(response_consumer, context, self._control) + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _Assembly( + collections.namedtuple( + '_Assembly', + ['methods', 'inlines', 'events', 'adaptations', 'messages'])): + """An intermediate structure created when creating a TestServiceDigest.""" + + +def _assemble( + scenarios, names, inline_method_constructor, event_method_constructor, + adapter, control, pool): + """Creates an _Assembly from the given scenarios.""" + methods = [] + inlines = {} + events = {} + adaptations = {} + messages = {} + for name, scenario in scenarios.iteritems(): + if name in names: + raise ValueError('Repeated name "%s"!' % name) + + test_method = scenario[0] + inline_method = inline_method_constructor(test_method, control) + event_method = event_method_constructor(test_method, control, pool) + adaptation = adapter(test_method) + + methods.append(test_method) + inlines[name] = inline_method + events[name] = event_method + adaptations[name] = adaptation + messages[name] = scenario[1] + + return _Assembly(methods, inlines, events, adaptations, messages) + + +def digest(service, control, pool): + """Creates a TestServiceDigest from a TestService. + + Args: + service: A testing_service.TestService. + control: A testing_control.Control. + pool: If RPC methods should be serviced in a separate thread, a thread pool. + None if RPC methods should be serviced in the thread belonging to the + run-time that calls for their service. + + Returns: + A TestServiceDigest synthesized from the given service.TestService. + """ + names = set() + + unary_unary = _assemble( + service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, + _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) + names.update(set(unary_unary.inlines)) + + unary_stream = _assemble( + service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, + _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) + names.update(set(unary_stream.inlines)) + + stream_unary = _assemble( + service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, + _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) + names.update(set(stream_unary.inlines)) + + stream_stream = _assemble( + service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, + _EventStreamStreamMethod, _IDENTITY, control, pool) + names.update(set(stream_stream.inlines)) + + methods = list(unary_unary.methods) + methods.extend(unary_stream.methods) + methods.extend(stream_unary.methods) + methods.extend(stream_stream.methods) + adaptations = dict(unary_unary.adaptations) + adaptations.update(unary_stream.adaptations) + adaptations.update(stream_unary.adaptations) + adaptations.update(stream_stream.adaptations) + inlines = dict(unary_unary.inlines) + inlines.update(unary_stream.inlines) + inlines.update(stream_unary.inlines) + inlines.update(stream_stream.inlines) + events = dict(unary_unary.events) + events.update(unary_stream.events) + events.update(stream_unary.events) + events.update(stream_stream.events) + + return TestServiceDigest( + service.name(), + methods, + inlines, + events, + _MultiMethodImplementation(adaptations, control, pool), + unary_unary.messages, + unary_stream.messages, + stream_unary.messages, + stream_stream.messages) diff --git a/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py new file mode 100644 index 0000000000..0f0b0e3d52 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py @@ -0,0 +1,362 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import unittest + +from grpc.framework.face import interfaces +from grpc.framework.face.testing import callback as testing_callback +from grpc.framework.face.testing import control +from grpc.framework.face.testing import coverage +from grpc.framework.face.testing import digest +from grpc.framework.face.testing import stock_service +from grpc.framework.face.testing import test_case + +_TIMEOUT = 3 + + +class EventInvocationSynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + # pylint: disable=cell-var-from-loop + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + first_callback = testing_callback.Callback() + second_callback = testing_callback.Callback() + + def make_second_invocation(first_response): + first_callback.complete(first_response) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + + self.stub.event_value_in_value_out( + name, first_request, make_second_invocation, first_callback.abort, + _TIMEOUT) + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + first_callback = testing_callback.Callback() + second_request = test_messages.request() + second_callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, first_request, first_callback.complete, first_callback.abort, + _TIMEOUT) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + first_callback.block_until_terminated() + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + call = self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + call = self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + call, unused_request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) diff --git a/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py new file mode 100644 index 0000000000..21bf9a4248 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -0,0 +1,376 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import contextlib +import threading +import unittest + +from grpc.framework.face import exceptions +from grpc.framework.face.testing import control +from grpc.framework.face.testing import coverage +from grpc.framework.face.testing import digest +from grpc.framework.face.testing import stock_service +from grpc.framework.face.testing import test_case +from grpc.framework.foundation import future +from grpc.framework.foundation import logging_pool + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 10 + + +class _PauseableIterator(object): + + def __init__(self, upstream): + self._upstream = upstream + self._condition = threading.Condition() + self._paused = False + + @contextlib.contextmanager + def pause(self): + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._paused: + self._condition.wait() + return next(self._upstream) + + +class FutureInvocationAsynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + self.digest_pool.shutdown(wait=True) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + response = response_future.result() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_future = self.stub.future_stream_in_value_out( + name, request_iterator, _TIMEOUT) + response = response_future.result() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, request_iterator, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + first_response = first_response_future.result() + + test_messages.verify(first_request, first_response, self) + + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + second_response = second_response_future.result() + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + multi_callable = self.stub.unary_unary_multi_callable(name) + response_future = multi_callable.future(request, _TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + multi_callable = self.stub.stream_unary_multi_callable(name) + response_future = multi_callable.future(iter(requests), _TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + first_response = first_response_future.result() + second_response = second_response_future.result() + + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) diff --git a/src/python/grpcio/grpc/framework/face/testing/interfaces.py b/src/python/grpcio/grpc/framework/face/testing/interfaces.py new file mode 100644 index 0000000000..5932dabf1e --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/interfaces.py @@ -0,0 +1,117 @@ +# 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. + +"""Interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# cardinality is referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import + + +class Method(object): + """An RPC method to be used in tests of RPC implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identify the name of the method. + + Returns: + The name of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cardinality(self): + """Identify the cardinality of the method. + + Returns: + A cardinality.Cardinality value describing the streaming semantics of the + method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def request_class(self): + """Identify the class used for the method's request objects. + + Returns: + The class object of the class to which the method's request objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_class(self): + """Identify the class used for the method's response objects. + + Returns: + The class object of the class to which the method's response objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize the given request object. + + Args: + request: A request object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Synthesize a request object from a given bytestring. + + Args: + serialized_request: A bytestring deserializable into a request object + appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize the given response object. + + Args: + response: A response object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Synthesize a response object from a given bytestring. + + Args: + serialized_response: A bytestring deserializable into a response object + appropriate for this method. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/serial.py b/src/python/grpcio/grpc/framework/face/testing/serial.py new file mode 100644 index 0000000000..47fc5822de --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/serial.py @@ -0,0 +1,70 @@ +# 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. + +"""Utility for serialization in the context of test RPC services.""" + +import collections + + +class Serialization( + collections.namedtuple( + '_Serialization', + ['request_serializers', + 'request_deserializers', + 'response_serializers', + 'response_deserializers'])): + """An aggregation of serialization behaviors for an RPC service. + + Attributes: + request_serializers: A dict from method name to request object serializer + behavior. + request_deserializers: A dict from method name to request object + deserializer behavior. + response_serializers: A dict from method name to response object serializer + behavior. + response_deserializers: A dict from method name to response object + deserializer behavior. + """ + + +def serialization(methods): + """Creates a Serialization from a sequences of interfaces.Method objects.""" + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for method in methods: + name = method.name() + request_serializers[name] = method.serialize_request + request_deserializers[name] = method.deserialize_request + response_serializers[name] = method.serialize_response + response_deserializers[name] = method.deserialize_response + return Serialization( + request_serializers, request_deserializers, response_serializers, + response_deserializers) diff --git a/src/python/grpcio/grpc/framework/face/testing/service.py b/src/python/grpcio/grpc/framework/face/testing/service.py new file mode 100644 index 0000000000..bf54d41d66 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/service.py @@ -0,0 +1,337 @@ +# 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. + +"""Private interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc.framework.face.testing import interfaces + + +class UnaryUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context, control): + """Services an RPC that accepts one message and produces one message. + + Args: + request: The single request message for the RPC. + response_callback: A callback to be called to accept the response message + of the RPC. + context: An face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryUnaryTestMessages(object): + """A type for unary-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, response, test_case): + """Verifies that the computed response matches the given request. + + Args: + request: A request message. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class UnaryStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context, control): + """Services an RPC that takes one message and produces a stream of messages. + + Args: + request: The single request message for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryStreamTestMessages(object): + """A type for unary-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, responses, test_case): + """Verifies that the computed responses match the given request. + + Args: + request: A request message. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and responses do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context, control): + """Services an RPC that takes a stream of messages and produces one message. + + Args: + response_callback: A callback to be called to accept the response message + of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamUnaryTestMessages(object): + """A type for stream-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, response, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context, control): + """Services an RPC that accepts and produces streams of messages. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamStreamTestMessages(object): + """A type for stream-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, responses, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and responses do not match, indicating + that there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class TestService(object): + """A specification of implemented RPC methods to use in tests.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identifies the RPC service name used during the test. + + Returns: + The RPC service name to be used for the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_scenarios(self): + """Affords unary-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair + is a UnaryUnaryTestMethodImplementation object and the second element + is a sequence of UnaryUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_scenarios(self): + """Affords unary-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + UnaryStreamTestMethodImplementation object and the second element is a + sequence of UnaryStreamTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_scenarios(self): + """Affords stream-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamUnaryTestMethodImplementation object and the second element is a + sequence of StreamUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_scenarios(self): + """Affords stream-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamStreamTestMethodImplementation object and the second element is a + sequence of StreamStreamTestMethodMessages objects. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/stock_service.py b/src/python/grpcio/grpc/framework/face/testing/stock_service.py new file mode 100644 index 0000000000..61aaf444a0 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/stock_service.py @@ -0,0 +1,374 @@ +# 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. + +"""Examples of Python implementations of the stock.proto Stock service.""" + +from grpc.framework.common import cardinality +from grpc.framework.face.testing import service +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc._junkdrawer import stock_pb2 + +SYMBOL_FORMAT = 'test symbol:%03d' +STREAM_LENGTH = 400 + +# A test-appropriate security-pricing function. :-P +_price = lambda symbol_name: float(hash(symbol_name) % 4096) + + +def _get_last_trade_price(stock_request, stock_reply_callback, control, active): + """A unary-request, unary-response test method.""" + control.control() + if active(): + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol))) + else: + raise abandonment.Abandoned() + + +def _get_last_trade_price_multiple(stock_reply_consumer, control, active): + """A stream-request, stream-response test method.""" + def stock_reply_for_stock_request(stock_request): + control.control() + if active(): + return stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol)) + else: + raise abandonment.Abandoned() + return stream_util.TransformingConsumer( + stock_reply_for_stock_request, stock_reply_consumer) + + +def _watch_future_trades(stock_request, stock_reply_consumer, control, active): + """A unary-request, stream-response test method.""" + base_price = _price(stock_request.symbol) + for index in range(stock_request.num_trades_to_watch): + control.control() + if active(): + stock_reply_consumer.consume( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=base_price + index)) + else: + raise abandonment.Abandoned() + stock_reply_consumer.terminate() + + +def _get_highest_trade_price(stock_reply_callback, control, active): + """A stream-request, unary-response test method.""" + + class StockRequestConsumer(stream.Consumer): + """Keeps an ongoing record of the most valuable symbol yet consumed.""" + + def __init__(self): + self._symbol = None + self._price = None + + def consume(self, stock_request): + control.control() + if active(): + if self._price is None: + self._symbol = stock_request.symbol + self._price = _price(stock_request.symbol) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + self._symbol = stock_request.symbol + self._price = candidate_price + + def terminate(self): + control.control() + if active(): + if self._symbol is None: + raise ValueError() + else: + stock_reply_callback( + stock_pb2.StockReply(symbol=self._symbol, price=self._price)) + self._symbol = None + self._price = None + + def consume_and_terminate(self, stock_request): + control.control() + if active(): + if self._price is None: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, + price=_price(stock_request.symbol))) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=candidate_price)) + else: + stock_reply_callback( + stock_pb2.StockReply( + symbol=self._symbol, price=self._price)) + + self._symbol = None + self._price = None + + return StockRequestConsumer() + + +class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): + """GetLastTradePrice for use in tests.""" + + def name(self): + return 'GetLastTradePrice' + + def cardinality(self): + return cardinality.Cardinality.UNARY_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_callback, context, control): + _get_last_trade_price( + request, response_callback, control, context.is_active) + + +class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest(symbol=symbol) + + def verify(self, request, response, test_case): + test_case.assertEqual(request.symbol, response.symbol) + test_case.assertEqual(_price(request.symbol), response.price) + + +class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): + """GetLastTradePriceMultiple for use in tests.""" + + def name(self): + return 'GetLastTradePriceMultiple' + + def cardinality(self): + return cardinality.Cardinality.STREAM_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_consumer, context, control): + return _get_last_trade_price_multiple( + response_consumer, control, context.is_active) + + +class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): + """Pairs of message streams for use with GetLastTradePriceMultiple.""" + + def __init__(self): + self._index = 0 + + def requests(self): + base_index = self._index + self._index += 1 + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, responses, test_case): + test_case.assertEqual(len(requests), len(responses)) + for stock_request, stock_reply in zip(requests, responses): + test_case.assertEqual(stock_request.symbol, stock_reply.symbol) + test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) + + +class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): + """WatchFutureTrades for use in tests.""" + + def name(self): + return 'WatchFutureTrades' + + def cardinality(self): + return cardinality.Cardinality.UNARY_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_consumer, context, control): + _watch_future_trades(request, response_consumer, control, context.is_active) + + +class WatchFutureTradesMessages(service.UnaryStreamTestMessages): + """Pairs of a single request message and a sequence of response messages.""" + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest( + symbol=symbol, num_trades_to_watch=STREAM_LENGTH) + + def verify(self, request, responses, test_case): + test_case.assertEqual(STREAM_LENGTH, len(responses)) + base_price = _price(request.symbol) + for index, response in enumerate(responses): + test_case.assertEqual(base_price + index, response.price) + + +class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): + """GetHighestTradePrice for use in tests.""" + + def name(self): + return 'GetHighestTradePrice' + + def cardinality(self): + return cardinality.Cardinality.STREAM_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_callback, context, control): + return _get_highest_trade_price( + response_callback, control, context.is_active) + + +class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): + + def requests(self): + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, response, test_case): + price = None + symbol = None + for stock_request in requests: + current_symbol = stock_request.symbol + current_price = _price(current_symbol) + if price is None or price < current_price: + price = current_price + symbol = current_symbol + test_case.assertEqual(price, response.price) + test_case.assertEqual(symbol, response.symbol) + + +class StockTestService(service.TestService): + """A corpus of test data with one method of each RPC cardinality.""" + + def name(self): + return 'Stock' + + def unary_unary_scenarios(self): + return { + 'GetLastTradePrice': ( + GetLastTradePrice(), [GetLastTradePriceMessages()]), + } + + def unary_stream_scenarios(self): + return { + 'WatchFutureTrades': ( + WatchFutureTrades(), [WatchFutureTradesMessages()]), + } + + def stream_unary_scenarios(self): + return { + 'GetHighestTradePrice': ( + GetHighestTradePrice(), [GetHighestTradePriceMessages()]) + } + + def stream_stream_scenarios(self): + return { + 'GetLastTradePriceMultiple': ( + GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), + } + + +STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio/grpc/framework/face/testing/test_case.py b/src/python/grpcio/grpc/framework/face/testing/test_case.py new file mode 100644 index 0000000000..e60e3d1d40 --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/testing/test_case.py @@ -0,0 +1,80 @@ +# 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. + +"""Tools for creating tests of implementations of the Face layer.""" + +import abc + +# face_interfaces and interfaces are referenced in specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc.framework.face.testing import interfaces # pylint: disable=unused-import + + +class FaceTestCase(object): + """Describes a test of the Face Layer of RPC Framework. + + Concrete subclasses must also inherit from unittest.TestCase and from at least + one class that defines test methods. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + """Instantiates the Face Layer implementation under test. + + Args: + name: The service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + method_implementations: A dictionary from string RPC method name to + face_interfaces.MethodImplementation object specifying + implementation of an RPC method. + multi_method_implementation: An face_interfaces.MultiMethodImplementation + or None. + + Returns: + A sequence of length two the first element of which is a + face_interfaces.GenericStub (backed by the given method + implementations), and the second element of which is an arbitrary memo + object to be kept and passed to tearDownImplementation at the conclusion + of the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def tear_down_implementation(self, memo): + """Destroys the Face layer implementation under test. + + Args: + memo: The object from the second position of the return value of + set_up_implementation. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/utilities.py b/src/python/grpcio/grpc/framework/face/utilities.py new file mode 100644 index 0000000000..a63fe8c60d --- /dev/null +++ b/src/python/grpcio/grpc/framework/face/utilities.py @@ -0,0 +1,177 @@ +# 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. + +"""Utilities for RPC framework's face layer.""" + +import collections + +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.face import interfaces +from grpc.framework.foundation import stream + + +class _MethodImplementation( + interfaces.MethodImplementation, + collections.namedtuple( + '_MethodImplementation', + ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline', + 'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event', + 'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])): + pass + + +def unary_unary_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable value + that takes a request value and an interfaces.RpcContext object and + returns a response value. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior, + None, None, None, None, None, None, None) + + +def unary_stream_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value and an interfaces.RpcContext object and + returns an iterator of response values. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None, + behavior, None, None, None, None, None, None) + + +def stream_unary_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes an iterator of request values and an + interfaces.RpcContext object and returns a response value. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None, + behavior, None, None, None, None, None) + + +def stream_stream_inline(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes an iterator of request values and an + interfaces.RpcContext object and returns an iterator of response values. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None, + None, behavior, None, None, None, None) + + +def unary_unary_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-unary RPC method as a callable + value that takes a request value, a response callback to which to pass + the response value of the RPC, and an interfaces.RpcContext. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None, + None, None, behavior, None, None, None) + + +def unary_stream_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a unary-stream RPC method as a callable + value that takes a request value, a stream.Consumer to which to pass the + the response values of the RPC, and an interfaces.RpcContext. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None, + None, None, None, behavior, None, None) + + +def stream_unary_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-unary RPC method as a callable + value that takes a response callback to which to pass the response value + of the RPC and an interfaces.RpcContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None, + None, None, None, None, behavior, None) + + +def stream_stream_event(behavior): + """Creates an interfaces.MethodImplementation for the given behavior. + + Args: + behavior: The implementation of a stream-stream RPC method as a callable + value that takes a stream.Consumer to which to pass the response values + of the RPC and an interfaces.RpcContext and returns a stream.Consumer to + which the request values of the RPC should be passed. + + Returns: + An interfaces.MethodImplementation derived from the given behavior. + """ + return _MethodImplementation( + cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None, + None, None, None, None, None, behavior) diff --git a/src/python/grpcio/grpc/framework/foundation/__init__.py b/src/python/grpcio/grpc/framework/foundation/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/foundation/_later_test.py b/src/python/grpcio/grpc/framework/foundation/_later_test.py new file mode 100644 index 0000000000..6c2459e185 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/_later_test.py @@ -0,0 +1,151 @@ +# 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 of the later module.""" + +import threading +import time +import unittest + +from grpc.framework.foundation import later + +TICK = 0.1 + + +class LaterTest(unittest.TestCase): + + def test_simple_delay(self): + lock = threading.Lock() + cell = [0] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + time.sleep(TICK) + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(0, cell[0]) + time.sleep(TICK * 2) + self.assertTrue(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(1, cell[0]) + self.assertEqual(return_value, computation_future.result()) + + def test_callback(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + time.sleep(TICK * 2) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + callback_called[0] = False + future_passed_to_callback[0] = None + + computation_future.add_done_callback(callback) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + def test_cancel(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + computation_future.cancel() + self.assertTrue(computation_future.cancelled()) + self.assertFalse(computation_future.running()) + self.assertTrue(computation_future.done()) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].cancelled()) + + def test_result(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback_cell = [None] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + def callback(future_passed_to_callback): + with lock: + callback_called[0] = True + future_passed_to_callback_cell[0] = future_passed_to_callback + computation_future.add_done_callback(callback) + returned_value = computation_future.result() + self.assertEqual(return_value, returned_value) + + # The callback may not yet have been called! Sleep a tick. + time.sleep(TICK) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py b/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py new file mode 100644 index 0000000000..452802da6a --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py @@ -0,0 +1,64 @@ +# 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 grpc.framework.foundation.logging_pool.""" + +import unittest + +from grpc.framework.foundation import logging_pool + +_POOL_SIZE = 16 + + +class LoggingPoolTest(unittest.TestCase): + + def testUpAndDown(self): + pool = logging_pool.pool(_POOL_SIZE) + pool.shutdown(wait=True) + + with logging_pool.pool(_POOL_SIZE) as pool: + self.assertIsNotNone(pool) + + def testTaskExecuted(self): + test_list = [] + + with logging_pool.pool(_POOL_SIZE) as pool: + pool.submit(lambda: test_list.append(object())).result() + + self.assertTrue(test_list) + + def testException(self): + with logging_pool.pool(_POOL_SIZE) as pool: + raised_exception = pool.submit(lambda: 1/0).exception() + + self.assertIsNotNone(raised_exception) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/foundation/_timer_future.py b/src/python/grpcio/grpc/framework/foundation/_timer_future.py new file mode 100644 index 0000000000..2c9996aa9d --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/_timer_future.py @@ -0,0 +1,228 @@ +# 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. + +"""Affords a Future implementation based on Python's threading.Timer.""" + +import sys +import threading +import time + +from grpc.framework.foundation import future + + +class TimerFuture(future.Future): + """A Future implementation based around Timer objects.""" + + def __init__(self, compute_time, computation): + """Constructor. + + Args: + compute_time: The time after which to begin this future's computation. + computation: The computation to be performed within this Future. + """ + self._lock = threading.Lock() + self._compute_time = compute_time + self._computation = computation + self._timer = None + self._computing = False + self._computed = False + self._cancelled = False + self._result = None + self._exception = None + self._traceback = None + self._waiting = [] + + def _compute(self): + """Performs the computation embedded in this Future. + + Or doesn't, if the time to perform it has not yet arrived. + """ + with self._lock: + time_remaining = self._compute_time - time.time() + if 0 < time_remaining: + self._timer = threading.Timer(time_remaining, self._compute) + self._timer.start() + return + else: + self._computing = True + + try: + return_value = self._computation() + exception = None + traceback = None + except Exception as e: # pylint: disable=broad-except + return_value = None + exception = e + traceback = sys.exc_info()[2] + + with self._lock: + self._computing = False + self._computed = True + self._return_value = return_value + self._exception = exception + self._traceback = traceback + waiting = self._waiting + + for callback in waiting: + callback(self) + + def start(self): + """Starts this Future. + + This must be called exactly once, immediately after construction. + """ + with self._lock: + self._timer = threading.Timer( + self._compute_time - time.time(), self._compute) + self._timer.start() + + def cancel(self): + """See future.Future.cancel for specification.""" + with self._lock: + if self._computing or self._computed: + return False + elif self._cancelled: + return True + else: + self._timer.cancel() + self._cancelled = True + waiting = self._waiting + + for callback in waiting: + try: + callback(self) + except Exception: # pylint: disable=broad-except + pass + + return True + + def cancelled(self): + """See future.Future.cancelled for specification.""" + with self._lock: + return self._cancelled + + def running(self): + """See future.Future.running for specification.""" + with self._lock: + return not self._computed and not self._cancelled + + def done(self): + """See future.Future.done for specification.""" + with self._lock: + return self._computed or self._cancelled + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() + + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() + + def add_done_callback(self, fn): + """See future.Future.add_done_callback for specification.""" + with self._lock: + if not self._computed and not self._cancelled: + self._waiting.append(fn) + return + + fn(self) diff --git a/src/python/grpcio/grpc/framework/foundation/abandonment.py b/src/python/grpcio/grpc/framework/foundation/abandonment.py new file mode 100644 index 0000000000..960b4d06b4 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/abandonment.py @@ -0,0 +1,38 @@ +# 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. + +"""Utilities for indicating abandonment of computation.""" + + +class Abandoned(Exception): + """Indicates that some computation is being abandoned. + + Abandoning a computation is different than returning a value or raising + an exception indicating some operational or programming defect. + """ diff --git a/src/python/grpcio/grpc/framework/foundation/activated.py b/src/python/grpcio/grpc/framework/foundation/activated.py new file mode 100644 index 0000000000..426a71c705 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/activated.py @@ -0,0 +1,65 @@ +# 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. + +"""Interfaces related to streams of values or objects.""" + +import abc + + +class Activated(object): + """Interface for objects that may be started and stopped. + + Values implementing this type must also implement the context manager + protocol. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __enter__(self): + """See the context manager protocol for specification.""" + raise NotImplementedError() + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + """See the context manager protocol for specification.""" + raise NotImplementedError() + + @abc.abstractmethod + def start(self): + """Activates this object. + + Returns: + A value equal to the value returned by this object's __enter__ method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stop(self): + """Deactivates this object.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/callable_util.py b/src/python/grpcio/grpc/framework/foundation/callable_util.py new file mode 100644 index 0000000000..32b0751a01 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/callable_util.py @@ -0,0 +1,107 @@ +# 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. + +"""Utilities for working with callables.""" + +import abc +import collections +import enum +import functools +import logging + + +class Outcome(object): + """A sum type describing the outcome of some call. + + Attributes: + kind: One of Kind.RETURNED or Kind.RAISED respectively indicating that the + call returned a value or raised an exception. + return_value: The value returned by the call. Must be present if kind is + Kind.RETURNED. + exception: The exception raised by the call. Must be present if kind is + Kind.RAISED. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + """Identifies the general kind of the outcome of some call.""" + + RETURNED = object() + RAISED = object() + + +class _EasyOutcome( + collections.namedtuple( + '_EasyOutcome', ['kind', 'return_value', 'exception']), + Outcome): + """A trivial implementation of Outcome.""" + + +def _call_logging_exceptions(behavior, message, *args, **kwargs): + try: + return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs), None) + except Exception as e: # pylint: disable=broad-except + logging.exception(message) + return _EasyOutcome(Outcome.Kind.RAISED, None, e) + + +def with_exceptions_logged(behavior, message): + """Wraps a callable in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + + Returns: + A callable that when executed invokes the given behavior. The returned + callable takes the same arguments as the given behavior but returns a + future.Outcome describing whether the given behavior returned a value or + raised an exception. + """ + @functools.wraps(behavior) + def wrapped_behavior(*args, **kwargs): + return _call_logging_exceptions(behavior, message, *args, **kwargs) + return wrapped_behavior + + +def call_logging_exceptions(behavior, message, *args, **kwargs): + """Calls a behavior in a try-except that logs any exceptions it raises. + + Args: + behavior: Any callable. + message: A string to log if the behavior raises an exception. + *args: Positional arguments to pass to the given behavior. + **kwargs: Keyword arguments to pass to the given behavior. + + Returns: + An Outcome describing whether the given behavior returned a value or raised + an exception. + """ + return _call_logging_exceptions(behavior, message, *args, **kwargs) diff --git a/src/python/grpcio/grpc/framework/foundation/future.py b/src/python/grpcio/grpc/framework/foundation/future.py new file mode 100644 index 0000000000..bfc16fc1ea --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/future.py @@ -0,0 +1,236 @@ +# 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. + +"""A Future interface. + +Python doesn't have a Future interface in its standard library. In the absence +of such a standard, three separate, incompatible implementations +(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This +interface attempts to be as compatible as possible with +concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor +method. + +Unlike the concrete and implemented Future classes listed above, the Future +class defined in this module is an entirely abstract interface that anyone may +implement and use. + +The one known incompatibility between this interface and the interface of +concurrent.futures.Future is that this interface defines its own CancelledError +and TimeoutError exceptions rather than raising the implementation-private +concurrent.futures._base.CancelledError and the +built-in-but-only-in-3.3-and-later TimeoutError. +""" + +import abc + + +class TimeoutError(Exception): + """Indicates that a particular call timed out.""" + + +class CancelledError(Exception): + """Indicates that the computation underlying a Future was cancelled.""" + + +class Future(object): + """A representation of a computation in another control flow. + + Computations represented by a Future may be yet to be begun, may be ongoing, + or may have already completed. + """ + __metaclass__ = abc.ABCMeta + + # NOTE(nathaniel): This isn't the return type that I would want to have if it + # were up to me. Were this interface being written from scratch, the return + # type of this method would probably be a sum type like: + # + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT + # COMPLETED + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + @abc.abstractmethod + def cancel(self): + """Attempts to cancel the computation. + + This method does not block. + + Returns: + True if the computation has not yet begun, will not be allowed to take + place, and determination of both was possible without blocking. False + under all other circumstances including but not limited to the + computation's already having begun, the computation's already having + finished, and the computation's having been scheduled for execution on a + remote system for which a determination of whether or not it commenced + before being cancelled cannot be made without blocking. + """ + raise NotImplementedError() + + # NOTE(nathaniel): Here too this isn't the return type that I'd want this + # method to have if it were up to me. I think I'd go with another sum type + # like: + # + # NOT_CANCELLED (this object's cancel method hasn't been called) + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT + # COMPLETED + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + # + # Notice how giving the cancel method the right semantics obviates most + # reasons for this method to exist. + @abc.abstractmethod + def cancelled(self): + """Describes whether the computation was cancelled. + + This method does not block. + + Returns: + True if the computation was cancelled any time before its result became + immediately available. False under all other circumstances including but + not limited to this object's cancel method not having been called and + the computation's result having become immediately available. + """ + raise NotImplementedError() + + @abc.abstractmethod + def running(self): + """Describes whether the computation is taking place. + + This method does not block. + + Returns: + True if the computation is scheduled to take place in the future or is + taking place now, or False if the computation took place in the past or + was cancelled. + """ + raise NotImplementedError() + + # NOTE(nathaniel): These aren't quite the semantics I'd like here either. I + # would rather this only returned True in cases in which the underlying + # computation completed successfully. A computation's having been cancelled + # conflicts with considering that computation "done". + @abc.abstractmethod + def done(self): + """Describes whether the computation has taken place. + + This method does not block. + + Returns: + True if the computation is known to have either completed or have been + unscheduled or interrupted. False if the computation may possibly be + executing or scheduled to execute later. + """ + raise NotImplementedError() + + @abc.abstractmethod + def result(self, timeout=None): + """Accesses the outcome of the computation or raises its exception. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + finish or be cancelled, or None if this method should block until the + computation has finished or is cancelled no matter how long that takes. + + Returns: + The return value of the computation. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + Exception: If the computation raised an exception, this call will raise + the same exception. + """ + raise NotImplementedError() + + @abc.abstractmethod + def exception(self, timeout=None): + """Return the exception raised by the computation. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. + + Returns: + The exception raised by the computation, or None if the computation did + not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + """ + raise NotImplementedError() + + @abc.abstractmethod + def traceback(self, timeout=None): + """Access the traceback of the exception raised by the computation. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. + + Returns: + The traceback of the exception raised by the computation, or None if the + computation did not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + """ + raise NotImplementedError() + + @abc.abstractmethod + def add_done_callback(self, fn): + """Adds a function to be called at completion of the computation. + + The callback will be passed this Future object describing the outcome of + the computation. + + If the computation has already completed, the callback will be called + immediately. + + Args: + fn: A callable taking a this Future object as its single parameter. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/later.py b/src/python/grpcio/grpc/framework/foundation/later.py new file mode 100644 index 0000000000..1d1e065041 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/later.py @@ -0,0 +1,51 @@ +# 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. + +"""Enables scheduling execution at a later time.""" + +import time + +from grpc.framework.foundation import _timer_future + + +def later(delay, computation): + """Schedules later execution of a callable. + + Args: + delay: Any numeric value. Represents the minimum length of time in seconds + to allow to pass before beginning the computation. No guarantees are made + about the maximum length of time that will pass. + computation: A callable that accepts no arguments. + + Returns: + A Future representing the scheduled computation. + """ + timer_future = _timer_future.TimerFuture(time.time() + delay, computation) + timer_future.start() + return timer_future diff --git a/src/python/grpcio/grpc/framework/foundation/logging_pool.py b/src/python/grpcio/grpc/framework/foundation/logging_pool.py new file mode 100644 index 0000000000..7c7a6eebfc --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/logging_pool.py @@ -0,0 +1,83 @@ +# 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. + +"""A thread pool that logs exceptions raised by tasks executed within it.""" + +import functools +import logging + +from concurrent import futures + + +def _wrap(behavior): + """Wraps an arbitrary callable behavior in exception-logging.""" + @functools.wraps(behavior) + def _wrapping(*args, **kwargs): + try: + return behavior(*args, **kwargs) + except Exception as e: + logging.exception('Unexpected exception from task run in logging pool!') + raise + return _wrapping + + +class _LoggingPool(object): + """An exception-logging futures.ThreadPoolExecutor-compatible thread pool.""" + + def __init__(self, backing_pool): + self._backing_pool = backing_pool + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._backing_pool.shutdown(wait=True) + + def submit(self, fn, *args, **kwargs): + return self._backing_pool.submit(_wrap(fn), *args, **kwargs) + + def map(self, func, *iterables, **kwargs): + return self._backing_pool.map( + _wrap(func), *iterables, timeout=kwargs.get('timeout', None)) + + def shutdown(self, wait=True): + self._backing_pool.shutdown(wait=wait) + + +def pool(max_workers): + """Creates a thread pool that logs exceptions raised by the tasks within it. + + Args: + max_workers: The maximum number of worker threads to allow the pool. + + Returns: + A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions + raised by the tasks executed within it. + """ + return _LoggingPool(futures.ThreadPoolExecutor(max_workers)) diff --git a/src/python/grpcio/grpc/framework/foundation/relay.py b/src/python/grpcio/grpc/framework/foundation/relay.py new file mode 100644 index 0000000000..9c23946552 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/relay.py @@ -0,0 +1,175 @@ +# 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. + +"""Implementations of in-order work deference.""" + +import abc +import enum +import threading + +from grpc.framework.foundation import activated +from grpc.framework.foundation import logging_pool + +_NULL_BEHAVIOR = lambda unused_value: None + + +class Relay(object): + """Performs work submitted to it in another thread. + + Performs work in the order in which work was submitted to it; otherwise there + would be no reason to use an implementation of this interface instead of a + thread pool. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def add_value(self, value): + """Adds a value to be passed to the behavior registered with this Relay. + + Args: + value: A value that will be passed to a call made in another thread to the + behavior registered with this Relay. + """ + raise NotImplementedError() + + @abc.abstractmethod + def set_behavior(self, behavior): + """Sets the behavior that this Relay should call when passed values. + + Args: + behavior: The behavior that this Relay should call in another thread when + passed a value, or None to have passed values ignored. + """ + raise NotImplementedError() + + +class _PoolRelay(activated.Activated, Relay): + + @enum.unique + class _State(enum.Enum): + INACTIVE = 'inactive' + IDLE = 'idle' + SPINNING = 'spinning' + + def __init__(self, pool, behavior): + self._condition = threading.Condition() + self._pool = pool + self._own_pool = pool is None + self._state = _PoolRelay._State.INACTIVE + self._activated = False + self._spinning = False + self._values = [] + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _spin(self, behavior, value): + while True: + behavior(value) + with self._condition: + if self._values: + value = self._values.pop(0) + behavior = self._behavior + else: + self._state = _PoolRelay._State.IDLE + self._condition.notify_all() + break + + def add_value(self, value): + with self._condition: + if self._state is _PoolRelay._State.INACTIVE: + raise ValueError('add_value not valid on inactive Relay!') + elif self._state is _PoolRelay._State.IDLE: + self._pool.submit(self._spin, self._behavior, value) + self._state = _PoolRelay._State.SPINNING + else: + self._values.append(value) + + def set_behavior(self, behavior): + with self._condition: + self._behavior = _NULL_BEHAVIOR if behavior is None else behavior + + def _start(self): + with self._condition: + self._state = _PoolRelay._State.IDLE + if self._own_pool: + self._pool = logging_pool.pool(1) + return self + + def _stop(self): + with self._condition: + while self._state is _PoolRelay._State.SPINNING: + self._condition.wait() + if self._own_pool: + self._pool.shutdown(wait=True) + self._state = _PoolRelay._State.INACTIVE + + def __enter__(self): + return self._start() + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop() + return False + + def start(self): + return self._start() + + def stop(self): + self._stop() + + +def relay(behavior): + """Creates a Relay. + + Args: + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(None, behavior) + + +def pool_relay(pool, behavior): + """Creates a Relay that uses a given thread pool. + + This object will make use of at most one thread in the given pool. + + Args: + pool: A futures.ThreadPoolExecutor for use by the created Relay. + behavior: The behavior to be called by the created Relay, or None to have + passed values dropped until a different behavior is given to the returned + Relay later. + + Returns: + An object that is both an activated.Activated and a Relay. The object is + only valid for use as a Relay when activated. + """ + return _PoolRelay(pool, behavior) diff --git a/src/python/grpcio/grpc/framework/foundation/stream.py b/src/python/grpcio/grpc/framework/foundation/stream.py new file mode 100644 index 0000000000..75c0cf145b --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/stream.py @@ -0,0 +1,60 @@ +# 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. + +"""Interfaces related to streams of values or objects.""" + +import abc + + +class Consumer(object): + """Interface for consumers of finite streams of values or objects.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def consume(self, value): + """Accepts a value. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def terminate(self): + """Indicates to this Consumer that no more values will be supplied.""" + raise NotImplementedError() + + @abc.abstractmethod + def consume_and_terminate(self, value): + """Supplies a value and signals that no more values will be supplied. + + Args: + value: Any value accepted by this Consumer. + """ + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/stream_testing.py b/src/python/grpcio/grpc/framework/foundation/stream_testing.py new file mode 100644 index 0000000000..098a53d5e7 --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/stream_testing.py @@ -0,0 +1,73 @@ +# 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. + +"""Utilities for testing stream-related code.""" + +from grpc.framework.foundation import stream + + +class TestConsumer(stream.Consumer): + """A stream.Consumer instrumented for testing. + + Attributes: + calls: A sequence of value-termination pairs describing the history of calls + made on this object. + """ + + def __init__(self): + self.calls = [] + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + self.calls.append((value, False)) + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + self.calls.append((None, True)) + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + self.calls.append((value, True)) + + def is_legal(self): + """Reports whether or not a legal sequence of calls has been made.""" + terminated = False + for value, terminal in self.calls: + if terminated: + return False + elif terminal: + terminated = True + elif value is None: + return False + else: # pylint: disable=useless-else-on-loop + return True + + def values(self): + """Returns the sequence of values that have been passed to this Consumer.""" + return [value for value, _ in self.calls if value] diff --git a/src/python/grpcio/grpc/framework/foundation/stream_util.py b/src/python/grpcio/grpc/framework/foundation/stream_util.py new file mode 100644 index 0000000000..2210e4efcf --- /dev/null +++ b/src/python/grpcio/grpc/framework/foundation/stream_util.py @@ -0,0 +1,160 @@ +# 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. + +"""Helpful utilities related to the stream module.""" + +import logging +import threading + +from grpc.framework.foundation import stream + +_NO_VALUE = object() + + +class TransformingConsumer(stream.Consumer): + """A stream.Consumer that passes a transformation of its input to another.""" + + def __init__(self, transformation, downstream): + self._transformation = transformation + self._downstream = downstream + + def consume(self, value): + self._downstream.consume(self._transformation(value)) + + def terminate(self): + self._downstream.terminate() + + def consume_and_terminate(self, value): + self._downstream.consume_and_terminate(self._transformation(value)) + + +class IterableConsumer(stream.Consumer): + """A Consumer that when iterated over emits the values it has consumed.""" + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._active = True + + def consume(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._condition.notify() + + def terminate(self): + with self._condition: + self._active = False + self._condition.notify() + + def consume_and_terminate(self, stock_reply): + with self._condition: + if self._active: + self._values.append(stock_reply) + self._active = False + self._condition.notify() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._active and not self._values: + self._condition.wait() + if self._values: + return self._values.pop(0) + else: + raise StopIteration() + + +class ThreadSwitchingConsumer(stream.Consumer): + """A Consumer decorator that affords serialization and asynchrony.""" + + def __init__(self, sink, pool): + self._lock = threading.Lock() + self._sink = sink + self._pool = pool + # True if self._spin has been submitted to the pool to be called once and + # that call has not yet returned, False otherwise. + self._spinning = False + self._values = [] + self._active = True + + def _spin(self, sink, value, terminate): + while True: + try: + if value is _NO_VALUE: + sink.terminate() + elif terminate: + sink.consume_and_terminate(value) + else: + sink.consume(value) + except Exception as e: # pylint:disable=broad-except + logging.exception(e) + + with self._lock: + if terminate: + self._spinning = False + return + elif self._values: + value = self._values.pop(0) + terminate = not self._values and not self._active + elif not self._active: + value = _NO_VALUE + terminate = True + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._active: + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, False) + self._spinning = True + + def terminate(self): + with self._lock: + if self._active: + self._active = False + if not self._spinning: + self._pool.submit(self._spin, self._sink, _NO_VALUE, True) + self._spinning = True + + def consume_and_terminate(self, value): + with self._lock: + if self._active: + self._active = False + if self._spinning: + self._values.append(value) + else: + self._pool.submit(self._spin, self._sink, value, True) + self._spinning = True diff --git a/src/python/grpcio/grpc/framework/interfaces/__init__.py b/src/python/grpcio/grpc/framework/interfaces/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/links/__init__.py b/src/python/grpcio/grpc/framework/interfaces/links/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio/grpc/framework/interfaces/links/links.py b/src/python/grpcio/grpc/framework/interfaces/links/links.py new file mode 100644 index 0000000000..5ebbac8a6f --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/links.py @@ -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. + +"""The low-level ticket-exchanging-links interface of RPC Framework.""" + +import abc +import collections +import enum + + +class Ticket( + collections.namedtuple( + 'Ticket', + ['operation_id', 'sequence_number', 'group', 'method', 'subscription', + 'timeout', 'allowance', 'initial_metadata', 'payload', + 'terminal_metadata', 'code', 'message', 'termination'])): + """A sum type for all values sent from a front to a back. + + Attributes: + operation_id: A unique-with-respect-to-equality hashable object identifying + a particular operation. + sequence_number: A zero-indexed integer sequence number identifying the + ticket's place in the stream of tickets sent in one direction for the + particular operation. + group: The group to which the method of the operation belongs. Must be + present in the first ticket from invocation side to service side. Ignored + for all other tickets exchanged during the operation. + method: The name of an operation. Must be present in the first ticket from + invocation side to service side. Ignored for all other tickets exchanged + during the operation. + subscription: A Subscription value describing the interest one side has in + receiving information from the other side. Must be present in the first + ticket from either side. Ignored for all other tickets exchanged during + the operation. + timeout: A nonzero length of time (measured from the beginning of the + operation) to allow for the entire operation. Must be present in the first + ticket from invocation side to service side. Optional for all other + tickets exchanged during the operation. Receipt of a value from the other + side of the operation indicates the value in use by that side. Setting a + value on a later ticket allows either side to request time extensions (or + even time reductions!) on in-progress operations. + allowance: A positive integer granting permission for a number of payloads + to be transmitted to the communicating side of the operation, or None if + no additional allowance is being granted with this ticket. + initial_metadata: An optional metadata value communicated from one side to + the other at the beginning of the operation. May be non-None in at most + one ticket from each side. Any non-None value must appear no later than + the first payload value. + payload: A customer payload object. May be None. + terminal_metadata: A metadata value comminicated from one side to the other + at the end of the operation. May be non-None in the same ticket as + the code and message, but must be None for all earlier tickets. + code: A value communicated at operation completion. May be None. + message: A value communicated at operation completion. May be None. + termination: A Termination value describing the end of the operation, or + None if the operation has not yet terminated. If set, no further tickets + may be sent in the same direction. + """ + + @enum.unique + class Subscription(enum.Enum): + """Identifies the level of subscription of a side of an operation.""" + + NONE = 'none' + TERMINATION = 'termination' + FULL = 'full' + + @enum.unique + class Termination(enum.Enum): + """Identifies the termination of an operation.""" + + COMPLETION = 'completion' + CANCELLATION = 'cancellation' + EXPIRATION = 'expiration' + LOCAL_SHUTDOWN = 'local shutdown' + RECEPTION_FAILURE = 'reception failure' + TRANSMISSION_FAILURE = 'transmission failure' + LOCAL_FAILURE = 'local failure' + REMOTE_FAILURE = 'remote failure' + + +class Link(object): + """Accepts and emits tickets.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def accept_ticket(self, ticket): + """Accept a Ticket. + + Args: + ticket: Any Ticket. + """ + raise NotImplementedError() + + @abc.abstractmethod + def join_link(self, link): + """Mates this object with a peer with which it will exchange tickets.""" + raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py b/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py new file mode 100644 index 0000000000..bf1f09d99d --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py @@ -0,0 +1,333 @@ +# 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 of the links interface of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.common import test_constants +from grpc.framework.interfaces.links import links +from grpc.framework.interfaces.links import test_utilities + + +def at_least_n_payloads_received_predicate(n): + def predicate(ticket_sequence): + payload_count = 0 + for ticket in ticket_sequence: + if ticket.payload is not None: + payload_count += 1 + if n <= payload_count: + return True + else: + return False + return predicate + + +def terminated(ticket_sequence): + return ticket_sequence and ticket_sequence[-1].termination is not None + +_TRANSMISSION_GROUP = 'test.Group' +_TRANSMISSION_METHOD = 'TestMethod' + + +class TransmissionTest(object): + """Tests ticket transmission between two connected links. + + This class must be mixed into a unittest.TestCase that implements the abstract + methods it provides. + """ + __metaclass__ = abc.ABCMeta + + # This is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + @abc.abstractmethod + def create_transmitting_links(self): + """Creates two connected links for use in this test. + + Returns: + Two links.Links, the first of which will be used on the invocation side + of RPCs and the second of which will be used on the service side of + RPCs. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + """Destroys the two connected links created for this test. + + + Args: + invocation_side_link: The link used on the invocation side of RPCs in + this test. + service_side_link: The link used on the service side of RPCs in this + test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_initial_metadata(self): + """Creates a value for use as invocation-side initial metadata. + + Returns: + A metadata value appropriate for use as invocation-side initial metadata + or None if invocation-side initial metadata transmission is not + supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_terminal_metadata(self): + """Creates a value for use as invocation-side terminal metadata. + + Returns: + A metadata value appropriate for use as invocation-side terminal + metadata or None if invocation-side terminal metadata transmission is + not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_initial_metadata(self): + """Creates a value for use as service-side initial metadata. + + Returns: + A metadata value appropriate for use as service-side initial metadata or + None if service-side initial metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_terminal_metadata(self): + """Creates a value for use as service-side terminal metadata. + + Returns: + A metadata value appropriate for use as service-side terminal metadata or + None if service-side terminal metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_completion(self): + """Creates values for use as invocation-side code and message. + + Returns: + An invocation-side code value and an invocation-side message value. + Either or both may be None if invocation-side code and/or + invocation-side message transmission is not supported by the links + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_completion(self): + """Creates values for use as service-side code and message. + + Returns: + A service-side code value and a service-side message value. Either or + both may be None if service-side code and/or service-side message + transmission is not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + """Asserts that transmitted_metadata contains original_metadata. + + Args: + original_metadata: A metadata object used in this test. + transmitted_metadata: A metadata object obtained after transmission + through the system under test. + + Raises: + AssertionError: if the transmitted_metadata object does not contain + original_metadata. + """ + raise NotImplementedError() + + def group_and_method(self): + """Returns the group and method used in this test case. + + Returns: + A pair of the group and method used in this test case. + """ + return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD + + def serialize_request(self, request): + """Serializes a request value used in this test case. + + Args: + request: A request value created by this test case. + + Returns: + A bytestring that is the serialization of the given request. + """ + return request + + def deserialize_request(self, serialized_request): + """Deserializes a request value used in this test case. + + Args: + serialized_request: A bytestring that is the serialization of some request + used in this test case. + + Returns: + The request value encoded by the given bytestring. + """ + return serialized_request + + def serialize_response(self, response): + """Serializes a response value used in this test case. + + Args: + response: A response value created by this test case. + + Returns: + A bytestring that is the serialization of the given response. + """ + return response + + def deserialize_response(self, serialized_response): + """Deserializes a response value used in this test case. + + Args: + serialized_response: A bytestring that is the serialization of some + response used in this test case. + + Returns: + The response value encoded by the given bytestring. + """ + return serialized_response + + def _assert_is_valid_metadata_payload_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata): + initial_metadata_seen = False + seen_payloads = [] + terminal_metadata_seen = False + + for ticket in ticket_sequence: + if ticket.initial_metadata is not None: + self.assertFalse(initial_metadata_seen) + self.assertFalse(seen_payloads) + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(initial_metadata, ticket.initial_metadata) + initial_metadata_seen = True + + if ticket.payload is not None: + self.assertFalse(terminal_metadata_seen) + seen_payloads.append(ticket.payload) + + if ticket.terminal_metadata is not None: + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(terminal_metadata, ticket.terminal_metadata) + terminal_metadata_seen = True + self.assertSequenceEqual(payloads, seen_payloads) + + def _assert_is_valid_invocation_sequence( + self, ticket_sequence, group, method, payloads, initial_metadata, + terminal_metadata, termination): + self.assertLess(0, len(ticket_sequence)) + self.assertEqual(group, ticket_sequence[0].group) + self.assertEqual(method, ticket_sequence[0].method) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertIs(termination, ticket_sequence[-1].termination) + + def _assert_is_valid_service_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata, + code, message, termination): + self.assertLess(0, len(ticket_sequence)) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertEqual(code, ticket_sequence[-1].code) + self.assertEqual(message, ticket_sequence[-1].message) + self.assertIs(termination, ticket_sequence[-1].termination) + + def setUp(self): + self._invocation_link, self._service_link = self.create_transmitting_links() + self._invocation_mate = test_utilities.RecordingLink() + self._service_mate = test_utilities.RecordingLink() + self._invocation_link.join_link(self._invocation_mate) + self._service_link.join_link(self._service_mate) + + def tearDown(self): + self.destroy_transmitting_links(self._invocation_link, self._service_link) + + def testSimplestRoundTrip(self): + """Tests transmission of one ticket in each direction.""" + invocation_operation_id = object() + invocation_payload = b'\x07' * 1023 + timeout = test_constants.LONG_TIMEOUT + invocation_initial_metadata = self.create_invocation_initial_metadata() + invocation_terminal_metadata = self.create_invocation_terminal_metadata() + invocation_code, invocation_message = self.create_invocation_completion() + service_payload = b'\x08' * 1025 + service_initial_metadata = self.create_service_initial_metadata() + service_terminal_metadata = self.create_service_terminal_metadata() + service_code, service_message = self.create_service_completion() + + original_invocation_ticket = links.Ticket( + invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, + invocation_payload, invocation_terminal_metadata, invocation_code, + invocation_message, links.Ticket.Termination.COMPLETION) + self._invocation_link.accept_ticket(original_invocation_ticket) + + # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the + # invocation-side ticket sequence shouldn't require granting allowance for + # another payload. + self._service_mate.block_until_tickets_satisfy( + at_least_n_payloads_received_predicate(1)) + service_operation_id = self._service_mate.tickets()[0].operation_id + self._service_link.accept_ticket( + links.Ticket( + service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, + None, 1, None, None, None, None, None, None)) + + self._service_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_invocation_sequence( + self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + (invocation_payload,), invocation_initial_metadata, + invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) + + original_service_ticket = links.Ticket( + service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, + timeout, 0, service_initial_metadata, service_payload, + service_terminal_metadata, service_code, service_message, + links.Ticket.Termination.COMPLETION) + self._service_link.accept_ticket(original_service_ticket) + self._invocation_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_service_sequence( + self._invocation_mate.tickets(), (service_payload,), + service_initial_metadata, service_terminal_metadata, service_code, + service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py b/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py new file mode 100644 index 0000000000..6c2e3346aa --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py @@ -0,0 +1,66 @@ +# 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. + +"""State and behavior appropriate for use in tests.""" + +import threading + +from grpc.framework.interfaces.links import links + + +class RecordingLink(links.Link): + """A Link that records every ticket passed to it.""" + + def __init__(self): + self._condition = threading.Condition() + self._tickets = [] + + def accept_ticket(self, ticket): + with self._condition: + self._tickets.append(ticket) + self._condition.notify_all() + + def join_link(self, link): + pass + + def block_until_tickets_satisfy(self, predicate): + """Blocks until the received tickets satisfy the given predicate. + + Args: + predicate: A callable that takes a sequence of tickets and returns a + boolean value. + """ + with self._condition: + while not predicate(self._tickets): + self._condition.wait() + + def tickets(self): + """Returns a copy of the list of all tickets received by this Link.""" + with self._condition: + return tuple(self._tickets) diff --git a/src/python/grpcio/grpc/framework/interfaces/links/utilities.py b/src/python/grpcio/grpc/framework/interfaces/links/utilities.py new file mode 100644 index 0000000000..6e4fd76d93 --- /dev/null +++ b/src/python/grpcio/grpc/framework/interfaces/links/utilities.py @@ -0,0 +1,44 @@ +# 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. + +"""Utilities provided as part of the links interface.""" + +from grpc.framework.interfaces.links import links + + +class _NullLink(links.Link): + """A do-nothing links.Link.""" + + def accept_ticket(self, ticket): + pass + + def join_link(self, link): + pass + +NULL_LINK = _NullLink() diff --git a/src/python/grpcio/setup.cfg b/src/python/grpcio/setup.cfg new file mode 100644 index 0000000000..8f69613632 --- /dev/null +++ b/src/python/grpcio/setup.cfg @@ -0,0 +1,2 @@ +[build_ext] +inplace=1 diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py new file mode 100644 index 0000000000..1333ae0086 --- /dev/null +++ b/src/python/grpcio/setup.py @@ -0,0 +1,112 @@ +# 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. + +"""A setup module for the GRPC Python package.""" + +import os +import os.path +import sys + +from distutils import core as _core +import setuptools + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands + +# Use environment variables to determine whether or not the Cython extension +# should *use* Cython or use the generated C files. Note that this requires the +# C files to have been generated by building first *with* Cython support. +_BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) + +_C_EXTENSION_SOURCES = ( + '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 = ( + '.', +) + +_EXTENSION_LIBRARIES = ( + 'grpc', + 'gpr', +) +if not "darwin" in sys.platform: + _EXTENSION_LIBRARIES += ('rt',) + + +_C_EXTENSION_MODULE = _core.Extension( + 'grpc._adapter._c', sources=list(_C_EXTENSION_SOURCES), + include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES), + libraries=list(_EXTENSION_LIBRARIES), +) +_EXTENSION_MODULES = [_C_EXTENSION_MODULE] + +_PACKAGES = ( + setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) +) + +_PACKAGE_DIRECTORIES = { + '': '.', +} + +_INSTALL_REQUIRES = ( + 'enum34==1.0.4', + 'futures==2.2.0', + 'protobuf==3.0.0a3' +) + +_SETUP_REQUIRES = ( + 'sphinx>=1.3', +) + _INSTALL_REQUIRES + +_COMMAND_CLASS = { + 'doc': commands.SphinxDocumentation +} + +setuptools.setup( + name='grpcio', + version='0.10.0a0', + ext_modules=_EXTENSION_MODULES, + packages=list(_PACKAGES), + package_dir=_PACKAGE_DIRECTORIES, + install_requires=_INSTALL_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS +) diff --git a/src/python/grpcio_test/grpc_interop/__init__.py b/src/python/grpcio_test/grpc_interop/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py new file mode 100644 index 0000000000..825988a072 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py @@ -0,0 +1,57 @@ +# 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. + +"""Insecure client-server interoperability as a unit test.""" + +import unittest + +from grpc.early_adopter import implementations + +from grpc_interop import _interop_test_case +from grpc_interop import methods + + +class InsecureInteropTest( + _interop_test_case.InteropTestCase, + unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, 0) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port) + + def tearDown(self): + self.server.stop() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_interop/_interop_test_case.py b/src/python/grpcio_test/grpc_interop/_interop_test_case.py new file mode 100644 index 0000000000..ed8f7ef009 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_interop_test_case.py @@ -0,0 +1,61 @@ +# 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. + +"""Common code for unit tests of the interoperability test code.""" + +from grpc_interop import methods + + +class InteropTestCase(object): + """Unit test methods. + + This class must be mixed in with unittest.TestCase and a class that defines + setUp and tearDown methods that manage a stub attribute. + """ + + def testEmptyUnary(self): + methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub, None) + + def testLargeUnary(self): + methods.TestCase.LARGE_UNARY.test_interoperability(self.stub, None) + + def testServerStreaming(self): + methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub, None) + + def testClientStreaming(self): + methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub, None) + + def testPingPong(self): + methods.TestCase.PING_PONG.test_interoperability(self.stub, None) + + def testCancelAfterBegin(self): + methods.TestCase.CANCEL_AFTER_BEGIN.test_interoperability(self.stub, None) + + def testCancelAfterFirstResponse(self): + methods.TestCase.CANCEL_AFTER_FIRST_RESPONSE.test_interoperability(self.stub, None) diff --git a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py new file mode 100644 index 0000000000..a2682dee99 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/_secure_interop_test.py @@ -0,0 +1,64 @@ +# 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. + +"""Secure client-server interoperability as a unit test.""" + +import unittest + +from grpc.early_adopter import implementations + +from grpc_interop import _interop_test_case +from grpc_interop import methods +from grpc_interop import resources + +_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' + + +class SecureInteropTest( + _interop_test_case.InteropTestCase, + unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, 0, + private_key=resources.private_key(), + certificate_chain=resources.certificate_chain()) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port, + secure=True, root_certificates=resources.test_root_certificates(), + server_host_override=_SERVER_HOST_OVERRIDE) + + def tearDown(self): + self.server.stop() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_interop/client.py b/src/python/grpcio_test/grpc_interop/client.py new file mode 100644 index 0000000000..2dd2103cbe --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/client.py @@ -0,0 +1,110 @@ +# 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. + +"""The Python implementation of the GRPC interoperability test client.""" + +import argparse +from oauth2client import client as oauth2client_client + +from grpc.early_adopter import implementations + +from grpc_interop import methods +from grpc_interop import resources + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +def _args(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--server_host', help='the host to which to connect', type=str) + parser.add_argument( + '--server_port', help='the port to which to connect', type=int) + parser.add_argument( + '--test_case', help='the test case to execute', type=str) + parser.add_argument( + '--use_tls', help='require a secure connection', dest='use_tls', + action='store_true') + parser.add_argument( + '--use_test_ca', help='replace platform root CAs with ca.pem', + action='store_true') + parser.add_argument( + '--server_host_override', + help='the server host to which to claim to connect', type=str) + parser.add_argument('--oauth_scope', help='scope for OAuth tokens', type=str) + parser.add_argument( + '--default_service_account', + help='email address of the default service account', type=str) + return parser.parse_args() + +def _oauth_access_token(args): + credentials = oauth2client_client.GoogleCredentials.get_application_default() + scoped_credentials = credentials.create_scoped([args.oauth_scope]) + return scoped_credentials.get_access_token().access_token + +def _stub(args): + if args.oauth_scope: + metadata_transformer = lambda x: [('Authorization', 'Bearer %s' % _oauth_access_token(args))] + else: + metadata_transformer = lambda x: [] + if args.use_tls: + if args.use_test_ca: + root_certificates = resources.test_root_certificates() + else: + root_certificates = resources.prod_root_certificates() + + stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, + args.server_port, metadata_transformer=metadata_transformer, + secure=True, root_certificates=root_certificates, + server_host_override=args.server_host_override) + else: + stub = implementations.stub( + methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, + args.server_port, secure=False) + return stub + + +def _test_case_from_arg(test_case_arg): + for test_case in methods.TestCase: + if test_case_arg == test_case.value: + return test_case + else: + raise ValueError('No test case "%s"!' % test_case_arg) + + +def _test_interoperability(): + args = _args() + stub = _stub(args) + test_case = _test_case_from_arg(args.test_case) + test_case.test_interoperability(stub, args) + + +if __name__ == '__main__': + _test_interoperability() diff --git a/src/python/grpcio_test/grpc_interop/credentials/README b/src/python/grpcio_test/grpc_interop/credentials/README new file mode 100644 index 0000000000..cb20dcb49f --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/README @@ -0,0 +1 @@ +These are test keys *NOT* to be used in production. diff --git a/src/python/grpcio_test/grpc_interop/credentials/ca.pem b/src/python/grpcio_test/grpc_interop/credentials/ca.pem new file mode 100755 index 0000000000..6c8511a73c --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.key b/src/python/grpcio_test/grpc_interop/credentials/server1.key new file mode 100755 index 0000000000..143a5b8765 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.pem b/src/python/grpcio_test/grpc_interop/credentials/server1.pem new file mode 100755 index 0000000000..8e582e571f --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/credentials/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/python/grpcio_test/grpc_interop/empty_pb2.py b/src/python/grpcio_test/grpc_interop/empty_pb2.py new file mode 100644 index 0000000000..8c1ce2f13e --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/empty_pb2.py @@ -0,0 +1,63 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/empty.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/empty.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ctest/cpp/interop/empty.proto\x12\x0cgrpc.testing\"\x07\n\x05\x45mpty') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='grpc.testing.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=46, + serialized_end=53, +) + +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( + DESCRIPTOR = _EMPTY, + __module__ = 'test.cpp.interop.empty_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Empty) + )) +_sym_db.RegisterMessage(Empty) + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_interop/messages_pb2.py b/src/python/grpcio_test/grpc_interop/messages_pb2.py new file mode 100644 index 0000000000..0bf3d86a31 --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/messages_pb2.py @@ -0,0 +1,447 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/messages.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/messages.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ftest/cpp/interop/messages.proto\x12\x0cgrpc.testing\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"\xb1\x01\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"C\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"7\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\"\xb5\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload*?\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x12\x12\n\x0eUNCOMPRESSABLE\x10\x01\x12\n\n\x06RANDOM\x10\x02') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +_PAYLOADTYPE = _descriptor.EnumDescriptor( + name='PayloadType', + full_name='grpc.testing.PayloadType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='COMPRESSABLE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='UNCOMPRESSABLE', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RANDOM', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=836, + serialized_end=899, +) +_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE) + +PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE) +COMPRESSABLE = 0 +UNCOMPRESSABLE = 1 +RANDOM = 2 + + + +_PAYLOAD = _descriptor.Descriptor( + name='Payload', + full_name='grpc.testing.Payload', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='grpc.testing.Payload.type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='body', full_name='grpc.testing.Payload.body', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=49, + serialized_end=113, +) + + +_SIMPLEREQUEST = _descriptor.Descriptor( + name='SimpleRequest', + full_name='grpc.testing.SimpleRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=116, + serialized_end=293, +) + + +_SIMPLERESPONSE = _descriptor.Descriptor( + name='SimpleResponse', + full_name='grpc.testing.SimpleResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='username', full_name='grpc.testing.SimpleResponse.username', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=295, + serialized_end=390, +) + + +_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingInputCallRequest', + full_name='grpc.testing.StreamingInputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=392, + serialized_end=459, +) + + +_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingInputCallResponse', + full_name='grpc.testing.StreamingInputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=461, + serialized_end=522, +) + + +_RESPONSEPARAMETERS = _descriptor.Descriptor( + name='ResponseParameters', + full_name='grpc.testing.ResponseParameters', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='size', full_name='grpc.testing.ResponseParameters.size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=524, + serialized_end=579, +) + + +_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingOutputCallRequest', + full_name='grpc.testing.StreamingOutputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=582, + serialized_end=763, +) + + +_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingOutputCallResponse', + full_name='grpc.testing.StreamingOutputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=765, + serialized_end=834, +) + +_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD +DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST +DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE +DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE +DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS +DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE +DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE + +Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict( + DESCRIPTOR = _PAYLOAD, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Payload) + )) +_sym_db.RegisterMessage(Payload) + +SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict( + DESCRIPTOR = _SIMPLEREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest) + )) +_sym_db.RegisterMessage(SimpleRequest) + +SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict( + DESCRIPTOR = _SIMPLERESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse) + )) +_sym_db.RegisterMessage(SimpleResponse) + +StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest) + )) +_sym_db.RegisterMessage(StreamingInputCallRequest) + +StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse) + )) +_sym_db.RegisterMessage(StreamingInputCallResponse) + +ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict( + DESCRIPTOR = _RESPONSEPARAMETERS, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters) + )) +_sym_db.RegisterMessage(ResponseParameters) + +StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest) + )) +_sym_db.RegisterMessage(StreamingOutputCallRequest) + +StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse) + )) +_sym_db.RegisterMessage(StreamingOutputCallResponse) + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_interop/methods.py b/src/python/grpcio_test/grpc_interop/methods.py new file mode 100644 index 0000000000..f4c94685ee --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/methods.py @@ -0,0 +1,375 @@ +# 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. + +"""Implementations of interoperability test methods.""" + +import enum +import json +import os +import threading + +from oauth2client import client as oauth2client_client + +from grpc.framework.alpha import utilities + +from grpc_interop import empty_pb2 +from grpc_interop import messages_pb2 + +_TIMEOUT = 7 + + +def _empty_call(request, unused_context): + return empty_pb2.Empty() + +_CLIENT_EMPTY_CALL = utilities.unary_unary_invocation_description( + empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString) +_SERVER_EMPTY_CALL = utilities.unary_unary_service_description( + _empty_call, empty_pb2.Empty.FromString, + empty_pb2.Empty.SerializeToString) + + +def _unary_call(request, unused_context): + return messages_pb2.SimpleResponse( + payload=messages_pb2.Payload( + type=messages_pb2.COMPRESSABLE, + body=b'\x00' * request.response_size)) + +_CLIENT_UNARY_CALL = utilities.unary_unary_invocation_description( + messages_pb2.SimpleRequest.SerializeToString, + messages_pb2.SimpleResponse.FromString) +_SERVER_UNARY_CALL = utilities.unary_unary_service_description( + _unary_call, messages_pb2.SimpleRequest.FromString, + messages_pb2.SimpleResponse.SerializeToString) + + +def _streaming_output_call(request, unused_context): + for response_parameters in request.response_parameters: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.response_type, + body=b'\x00' * response_parameters.size)) + +_CLIENT_STREAMING_OUTPUT_CALL = utilities.unary_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_STREAMING_OUTPUT_CALL = utilities.unary_stream_service_description( + _streaming_output_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + + +def _streaming_input_call(request_iterator, unused_context): + aggregate_size = 0 + for request in request_iterator: + if request.payload and request.payload.body: + aggregate_size += len(request.payload.body) + return messages_pb2.StreamingInputCallResponse( + aggregated_payload_size=aggregate_size) + +_CLIENT_STREAMING_INPUT_CALL = utilities.stream_unary_invocation_description( + messages_pb2.StreamingInputCallRequest.SerializeToString, + messages_pb2.StreamingInputCallResponse.FromString) +_SERVER_STREAMING_INPUT_CALL = utilities.stream_unary_service_description( + _streaming_input_call, + messages_pb2.StreamingInputCallRequest.FromString, + messages_pb2.StreamingInputCallResponse.SerializeToString) + + +def _full_duplex_call(request_iterator, unused_context): + for request in request_iterator: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.payload.type, + body=b'\x00' * request.response_parameters[0].size)) + +_CLIENT_FULL_DUPLEX_CALL = utilities.stream_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_FULL_DUPLEX_CALL = utilities.stream_stream_service_description( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + +# NOTE(nathaniel): Apparently this is the same as the full-duplex call? +_CLIENT_HALF_DUPLEX_CALL = utilities.stream_stream_invocation_description( + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) +_SERVER_HALF_DUPLEX_CALL = utilities.stream_stream_service_description( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString) + + +SERVICE_NAME = 'grpc.testing.TestService' + +_EMPTY_CALL_METHOD_NAME = 'EmptyCall' +_UNARY_CALL_METHOD_NAME = 'UnaryCall' +_STREAMING_OUTPUT_CALL_METHOD_NAME = 'StreamingOutputCall' +_STREAMING_INPUT_CALL_METHOD_NAME = 'StreamingInputCall' +_FULL_DUPLEX_CALL_METHOD_NAME = 'FullDuplexCall' +_HALF_DUPLEX_CALL_METHOD_NAME = 'HalfDuplexCall' + +CLIENT_METHODS = { + _EMPTY_CALL_METHOD_NAME: _CLIENT_EMPTY_CALL, + _UNARY_CALL_METHOD_NAME: _CLIENT_UNARY_CALL, + _STREAMING_OUTPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_OUTPUT_CALL, + _STREAMING_INPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_INPUT_CALL, + _FULL_DUPLEX_CALL_METHOD_NAME: _CLIENT_FULL_DUPLEX_CALL, + _HALF_DUPLEX_CALL_METHOD_NAME: _CLIENT_HALF_DUPLEX_CALL, +} + +SERVER_METHODS = { + _EMPTY_CALL_METHOD_NAME: _SERVER_EMPTY_CALL, + _UNARY_CALL_METHOD_NAME: _SERVER_UNARY_CALL, + _STREAMING_OUTPUT_CALL_METHOD_NAME: _SERVER_STREAMING_OUTPUT_CALL, + _STREAMING_INPUT_CALL_METHOD_NAME: _SERVER_STREAMING_INPUT_CALL, + _FULL_DUPLEX_CALL_METHOD_NAME: _SERVER_FULL_DUPLEX_CALL, + _HALF_DUPLEX_CALL_METHOD_NAME: _SERVER_HALF_DUPLEX_CALL, +} + + +def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope): + with stub: + request = messages_pb2.SimpleRequest( + response_type=messages_pb2.COMPRESSABLE, response_size=314159, + payload=messages_pb2.Payload(body=b'\x00' * 271828), + fill_username=fill_username, fill_oauth_scope=fill_oauth_scope) + response_future = stub.UnaryCall.async(request, _TIMEOUT) + response = response_future.result() + if response.payload.type is not messages_pb2.COMPRESSABLE: + raise ValueError( + 'response payload type is "%s"!' % type(response.payload.type)) + if len(response.payload.body) != 314159: + raise ValueError( + 'response body of incorrect size %d!' % len(response.payload.body)) + return response + + +def _empty_unary(stub): + with stub: + response = stub.EmptyCall(empty_pb2.Empty(), _TIMEOUT) + if not isinstance(response, empty_pb2.Empty): + raise TypeError( + 'response is of type "%s", not empty_pb2.Empty!', type(response)) + + +def _large_unary(stub): + _large_unary_common_behavior(stub, False, False) + + +def _client_streaming(stub): + with stub: + payload_body_sizes = (27182, 8, 1828, 45904) + payloads = ( + messages_pb2.Payload(body=b'\x00' * size) + for size in payload_body_sizes) + requests = ( + messages_pb2.StreamingInputCallRequest(payload=payload) + for payload in payloads) + response = stub.StreamingInputCall(requests, _TIMEOUT) + if response.aggregated_payload_size != 74922: + raise ValueError( + 'incorrect size %d!' % response.aggregated_payload_size) + + +def _server_streaming(stub): + sizes = (31415, 9, 2653, 58979) + + with stub: + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=( + messages_pb2.ResponseParameters(size=sizes[0]), + messages_pb2.ResponseParameters(size=sizes[1]), + messages_pb2.ResponseParameters(size=sizes[2]), + messages_pb2.ResponseParameters(size=sizes[3]), + )) + response_iterator = stub.StreamingOutputCall(request, _TIMEOUT) + for index, response in enumerate(response_iterator): + if response.payload.type != messages_pb2.COMPRESSABLE: + raise ValueError( + 'response body of invalid type %s!' % response.payload.type) + if len(response.payload.body) != sizes[index]: + raise ValueError( + 'response body of invalid size %d!' % len(response.payload.body)) + +def _cancel_after_begin(stub): + with stub: + sizes = (27182, 8, 1828, 45904) + payloads = [messages_pb2.Payload(body=b'\x00' * size) for size in sizes] + requests = [messages_pb2.StreamingInputCallRequest(payload=payload) + for payload in payloads] + responses = stub.StreamingInputCall.async(requests, _TIMEOUT) + responses.cancel() + if not responses.cancelled(): + raise ValueError('expected call to be cancelled') + + +class _Pipe(object): + + def __init__(self): + self._condition = threading.Condition() + self._values = [] + self._open = True + + def __iter__(self): + return self + + def next(self): + with self._condition: + while not self._values and self._open: + self._condition.wait() + if self._values: + return self._values.pop(0) + else: + raise StopIteration() + + def add(self, value): + with self._condition: + self._values.append(value) + self._condition.notify() + + def close(self): + with self._condition: + self._open = False + self._condition.notify() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + +def _ping_pong(stub): + request_response_sizes = (31415, 9, 2653, 58979) + request_payload_sizes = (27182, 8, 1828, 45904) + + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) + print 'Starting ping-pong with response iterator %s' % response_iterator + for response_size, payload_size in zip( + request_response_sizes, request_payload_sizes): + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=(messages_pb2.ResponseParameters( + size=response_size),), + payload=messages_pb2.Payload(body=b'\x00' * payload_size)) + pipe.add(request) + response = next(response_iterator) + if response.payload.type != messages_pb2.COMPRESSABLE: + raise ValueError( + 'response body of invalid type %s!' % response.payload.type) + if len(response.payload.body) != response_size: + raise ValueError( + 'response body of invalid size %d!' % len(response.payload.body)) + + +def _cancel_after_first_response(stub): + request_response_sizes = (31415, 9, 2653, 58979) + request_payload_sizes = (27182, 8, 1828, 45904) + with stub, _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) + + response_size = request_response_sizes[0] + payload_size = request_payload_sizes[0] + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=(messages_pb2.ResponseParameters( + size=response_size),), + payload=messages_pb2.Payload(body=b'\x00' * payload_size)) + pipe.add(request) + response = next(response_iterator) + # We test the contents of `response` in the Ping Pong test - don't check + # them here. + response_iterator.cancel() + + try: + next(response_iterator) + except Exception: + pass + else: + raise ValueError('expected call to be cancelled') + + +def _compute_engine_creds(stub, args): + response = _large_unary_common_behavior(stub, True, True) + if args.default_service_account != response.username: + raise ValueError( + 'expected username %s, got %s' % (args.default_service_account, + response.username)) + + +def _service_account_creds(stub, args): + json_key_filename = os.environ[ + oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] + wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] + response = _large_unary_common_behavior(stub, True, True) + if wanted_email != response.username: + raise ValueError( + 'expected username %s, got %s' % (wanted_email, response.username)) + if args.oauth_scope.find(response.oauth_scope) == -1: + raise ValueError( + 'expected to find oauth scope "%s" in received "%s"' % + (response.oauth_scope, args.oauth_scope)) + + +@enum.unique +class TestCase(enum.Enum): + EMPTY_UNARY = 'empty_unary' + LARGE_UNARY = 'large_unary' + SERVER_STREAMING = 'server_streaming' + CLIENT_STREAMING = 'client_streaming' + PING_PONG = 'ping_pong' + CANCEL_AFTER_BEGIN = 'cancel_after_begin' + CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response' + COMPUTE_ENGINE_CREDS = 'compute_engine_creds' + SERVICE_ACCOUNT_CREDS = 'service_account_creds' + + def test_interoperability(self, stub, args): + if self is TestCase.EMPTY_UNARY: + _empty_unary(stub) + elif self is TestCase.LARGE_UNARY: + _large_unary(stub) + elif self is TestCase.SERVER_STREAMING: + _server_streaming(stub) + elif self is TestCase.CLIENT_STREAMING: + _client_streaming(stub) + elif self is TestCase.PING_PONG: + _ping_pong(stub) + elif self is TestCase.CANCEL_AFTER_BEGIN: + _cancel_after_begin(stub) + elif self is TestCase.CANCEL_AFTER_FIRST_RESPONSE: + _cancel_after_first_response(stub) + elif self is TestCase.COMPUTE_ENGINE_CREDS: + _compute_engine_creds(stub, args) + elif self is TestCase.SERVICE_ACCOUNT_CREDS: + _service_account_creds(stub, args) + else: + raise NotImplementedError('Test case "%s" not implemented!' % self.name) diff --git a/src/python/grpcio_test/grpc_interop/resources.py b/src/python/grpcio_test/grpc_interop/resources.py new file mode 100644 index 0000000000..2c3045313d --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/resources.py @@ -0,0 +1,56 @@ +# 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. + +"""Constants and functions for data used in interoperability testing.""" + +import os + +import pkg_resources + +_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem' +_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' +_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' + + +def test_root_certificates(): + return pkg_resources.resource_string( + __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) + + +def prod_root_certificates(): + return open(os.environ['SSL_CERT_FILE'], mode='rb').read() + + +def private_key(): + return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) + + +def certificate_chain(): + return pkg_resources.resource_string( + __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) diff --git a/src/python/grpcio_test/grpc_interop/server.py b/src/python/grpcio_test/grpc_interop/server.py new file mode 100644 index 0000000000..60f630a6be --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/server.py @@ -0,0 +1,74 @@ +# 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. + +"""The Python implementation of the GRPC interoperability test server.""" + +import argparse +import logging +import time + +from grpc.early_adopter import implementations + +from grpc_interop import methods +from grpc_interop import resources + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +def serve(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--port', help='the port on which to serve', type=int) + parser.add_argument( + '--use_tls', help='require a secure connection', dest='use_tls', + action='store_true') + args = parser.parse_args() + + if args.use_tls: + private_key = resources.private_key() + certificate_chain = resources.certificate_chain() + server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, args.port, + private_key=private_key, certificate_chain=certificate_chain) + else: + server = implementations.server( + methods.SERVICE_NAME, methods.SERVER_METHODS, args.port) + + server.start() + logging.info('Server serving.') + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except BaseException as e: + logging.info('Caught exception "%s"; stopping server...', e) + server.stop() + logging.info('Server stopped; exiting.') + +if __name__ == '__main__': + serve() diff --git a/src/python/grpcio_test/grpc_interop/test_pb2.py b/src/python/grpcio_test/grpc_interop/test_pb2.py new file mode 100644 index 0000000000..71325d5a9f --- /dev/null +++ b/src/python/grpcio_test/grpc_interop/test_pb2.py @@ -0,0 +1,178 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/test.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from test.cpp.interop import empty_pb2 as test_dot_cpp_dot_interop_dot_empty__pb2 +from test.cpp.interop import messages_pb2 as test_dot_cpp_dot_interop_dot_messages__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/test.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1btest/cpp/interop/test.proto\x12\x0cgrpc.testing\x1a\x1ctest/cpp/interop/empty.proto\x1a\x1ftest/cpp/interop/messages.proto2\xbb\x04\n\x0bTestService\x12\x35\n\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12\x46\n\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n\x12StreamingInputCall\x12\'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n\x0e\x46ullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01\x12i\n\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01') + , + dependencies=[test_dot_cpp_dot_interop_dot_empty__pb2.DESCRIPTOR,test_dot_cpp_dot_interop_dot_messages__pb2.DESCRIPTOR,]) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + + +import abc +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +class EarlyAdopterTestServiceServicer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def EmptyCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def UnaryCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def StreamingOutputCall(self, request, context): + raise NotImplementedError() + @abc.abstractmethod + def StreamingInputCall(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def FullDuplexCall(self, request_iterator, context): + raise NotImplementedError() + @abc.abstractmethod + def HalfDuplexCall(self, request_iterator, context): + raise NotImplementedError() +class EarlyAdopterTestServiceServer(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def start(self): + raise NotImplementedError() + @abc.abstractmethod + def stop(self): + raise NotImplementedError() +class EarlyAdopterTestServiceStub(object): + """""" + __metaclass__ = abc.ABCMeta + @abc.abstractmethod + def EmptyCall(self, request): + raise NotImplementedError() + EmptyCall.async = None + @abc.abstractmethod + def UnaryCall(self, request): + raise NotImplementedError() + UnaryCall.async = None + @abc.abstractmethod + def StreamingOutputCall(self, request): + raise NotImplementedError() + StreamingOutputCall.async = None + @abc.abstractmethod + def StreamingInputCall(self, request_iterator): + raise NotImplementedError() + StreamingInputCall.async = None + @abc.abstractmethod + def FullDuplexCall(self, request_iterator): + raise NotImplementedError() + FullDuplexCall.async = None + @abc.abstractmethod + def HalfDuplexCall(self, request_iterator): + raise NotImplementedError() + HalfDuplexCall.async = None +def early_adopter_create_TestService_server(servicer, port, private_key=None, certificate_chain=None): + import test.cpp.interop.empty_pb2 + import test.cpp.interop.empty_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + method_service_descriptions = { + "EmptyCall": utilities.unary_unary_service_description( + servicer.EmptyCall, + test.cpp.interop.empty_pb2.Empty.FromString, + test.cpp.interop.empty_pb2.Empty.SerializeToString, + ), + "FullDuplexCall": utilities.stream_stream_service_description( + servicer.FullDuplexCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "HalfDuplexCall": utilities.stream_stream_service_description( + servicer.HalfDuplexCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "StreamingInputCall": utilities.stream_unary_service_description( + servicer.StreamingInputCall, + test.cpp.interop.messages_pb2.StreamingInputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingInputCallResponse.SerializeToString, + ), + "StreamingOutputCall": utilities.unary_stream_service_description( + servicer.StreamingOutputCall, + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, + ), + "UnaryCall": utilities.unary_unary_service_description( + servicer.UnaryCall, + test.cpp.interop.messages_pb2.SimpleRequest.FromString, + test.cpp.interop.messages_pb2.SimpleResponse.SerializeToString, + ), + } + return implementations.server("grpc.testing.TestService", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) +def early_adopter_create_TestService_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): + import test.cpp.interop.empty_pb2 + import test.cpp.interop.empty_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + import test.cpp.interop.messages_pb2 + method_invocation_descriptions = { + "EmptyCall": utilities.unary_unary_invocation_description( + test.cpp.interop.empty_pb2.Empty.SerializeToString, + test.cpp.interop.empty_pb2.Empty.FromString, + ), + "FullDuplexCall": utilities.stream_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "HalfDuplexCall": utilities.stream_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "StreamingInputCall": utilities.stream_unary_invocation_description( + test.cpp.interop.messages_pb2.StreamingInputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingInputCallResponse.FromString, + ), + "StreamingOutputCall": utilities.unary_stream_invocation_description( + test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, + test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, + ), + "UnaryCall": utilities.unary_unary_invocation_description( + test.cpp.interop.messages_pb2.SimpleRequest.SerializeToString, + test.cpp.interop.messages_pb2.SimpleResponse.FromString, + ), + } + return implementations.stub("grpc.testing.TestService", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py new file mode 100644 index 0000000000..dbea0f76ae --- /dev/null +++ b/src/python/grpcio_test/setup.py @@ -0,0 +1,55 @@ +# 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. + +"""A setup module for the GRPC Python interop testing package.""" + +import setuptools + +_PACKAGES = setuptools.find_packages('.') + +_PACKAGE_DIRECTORIES = { + '': '.', +} + +_PACKAGE_DATA = { + 'grpc_interop': [ + 'credentials/ca.pem', 'credentials/server1.key', + 'credentials/server1.pem',] +} + +_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.10.0a0'] + +setuptools.setup( + name='grpcio_test', + version='0.0.1', + packages=_PACKAGES, + package_dir=_PACKAGE_DIRECTORIES, + package_data=_PACKAGE_DATA, + install_requires=_INSTALL_REQUIRES +) diff --git a/src/python/interop/interop/__init__.py b/src/python/interop/interop/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/interop/interop/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/interop/interop/_insecure_interop_test.py b/src/python/interop/interop/_insecure_interop_test.py deleted file mode 100644 index 98ea3a6648..0000000000 --- a/src/python/interop/interop/_insecure_interop_test.py +++ /dev/null @@ -1,57 +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. - -"""Insecure client-server interoperability as a unit test.""" - -import unittest - -from grpc.early_adopter import implementations - -from interop import _interop_test_case -from interop import methods - - -class InsecureInteropTest( - _interop_test_case.InteropTestCase, - unittest.TestCase): - - def setUp(self): - self.server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, 0) - self.server.start() - port = self.server.port() - self.stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port) - - def tearDown(self): - self.server.stop() - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/interop/interop/_interop_test_case.py b/src/python/interop/interop/_interop_test_case.py deleted file mode 100644 index f40ef0ec83..0000000000 --- a/src/python/interop/interop/_interop_test_case.py +++ /dev/null @@ -1,61 +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. - -"""Common code for unit tests of the interoperability test code.""" - -from interop import methods - - -class InteropTestCase(object): - """Unit test methods. - - This class must be mixed in with unittest.TestCase and a class that defines - setUp and tearDown methods that manage a stub attribute. - """ - - def testEmptyUnary(self): - methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub, None) - - def testLargeUnary(self): - methods.TestCase.LARGE_UNARY.test_interoperability(self.stub, None) - - def testServerStreaming(self): - methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub, None) - - def testClientStreaming(self): - methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub, None) - - def testPingPong(self): - methods.TestCase.PING_PONG.test_interoperability(self.stub, None) - - def testCancelAfterBegin(self): - methods.TestCase.CANCEL_AFTER_BEGIN.test_interoperability(self.stub, None) - - def testCancelAfterFirstResponse(self): - methods.TestCase.CANCEL_AFTER_FIRST_RESPONSE.test_interoperability(self.stub, None) diff --git a/src/python/interop/interop/_secure_interop_test.py b/src/python/interop/interop/_secure_interop_test.py deleted file mode 100644 index be7618f549..0000000000 --- a/src/python/interop/interop/_secure_interop_test.py +++ /dev/null @@ -1,64 +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. - -"""Secure client-server interoperability as a unit test.""" - -import unittest - -from grpc.early_adopter import implementations - -from interop import _interop_test_case -from interop import methods -from interop import resources - -_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' - - -class SecureInteropTest( - _interop_test_case.InteropTestCase, - unittest.TestCase): - - def setUp(self): - self.server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, 0, - private_key=resources.private_key(), - certificate_chain=resources.certificate_chain()) - self.server.start() - port = self.server.port() - self.stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, 'localhost', port, - secure=True, root_certificates=resources.test_root_certificates(), - server_host_override=_SERVER_HOST_OVERRIDE) - - def tearDown(self): - self.server.stop() - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/interop/interop/client.py b/src/python/interop/interop/client.py deleted file mode 100644 index 41f0d94539..0000000000 --- a/src/python/interop/interop/client.py +++ /dev/null @@ -1,110 +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. - -"""The Python implementation of the GRPC interoperability test client.""" - -import argparse -from oauth2client import client as oauth2client_client - -from grpc.early_adopter import implementations - -from interop import methods -from interop import resources - -_ONE_DAY_IN_SECONDS = 60 * 60 * 24 - - -def _args(): - parser = argparse.ArgumentParser() - parser.add_argument( - '--server_host', help='the host to which to connect', type=str) - parser.add_argument( - '--server_port', help='the port to which to connect', type=int) - parser.add_argument( - '--test_case', help='the test case to execute', type=str) - parser.add_argument( - '--use_tls', help='require a secure connection', dest='use_tls', - action='store_true') - parser.add_argument( - '--use_test_ca', help='replace platform root CAs with ca.pem', - action='store_true') - parser.add_argument( - '--server_host_override', - help='the server host to which to claim to connect', type=str) - parser.add_argument('--oauth_scope', help='scope for OAuth tokens', type=str) - parser.add_argument( - '--default_service_account', - help='email address of the default service account', type=str) - return parser.parse_args() - -def _oauth_access_token(args): - credentials = oauth2client_client.GoogleCredentials.get_application_default() - scoped_credentials = credentials.create_scoped([args.oauth_scope]) - return scoped_credentials.get_access_token().access_token - -def _stub(args): - if args.oauth_scope: - metadata_transformer = lambda x: [('Authorization', 'Bearer %s' % _oauth_access_token(args))] - else: - metadata_transformer = lambda x: [] - if args.use_tls: - if args.use_test_ca: - root_certificates = resources.test_root_certificates() - else: - root_certificates = resources.prod_root_certificates() - - stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, - args.server_port, metadata_transformer=metadata_transformer, - secure=True, root_certificates=root_certificates, - server_host_override=args.server_host_override) - else: - stub = implementations.stub( - methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host, - args.server_port, secure=False) - return stub - - -def _test_case_from_arg(test_case_arg): - for test_case in methods.TestCase: - if test_case_arg == test_case.value: - return test_case - else: - raise ValueError('No test case "%s"!' % test_case_arg) - - -def _test_interoperability(): - args = _args() - stub = _stub(args) - test_case = _test_case_from_arg(args.test_case) - test_case.test_interoperability(stub, args) - - -if __name__ == '__main__': - _test_interoperability() diff --git a/src/python/interop/interop/credentials/README b/src/python/interop/interop/credentials/README deleted file mode 100644 index cb20dcb49f..0000000000 --- a/src/python/interop/interop/credentials/README +++ /dev/null @@ -1 +0,0 @@ -These are test keys *NOT* to be used in production. diff --git a/src/python/interop/interop/credentials/ca.pem b/src/python/interop/interop/credentials/ca.pem deleted file mode 100755 index 6c8511a73c..0000000000 --- a/src/python/interop/interop/credentials/ca.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla -Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 -YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT -BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 -+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu -g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd -Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau -sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m -oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG -Dfcog5wrJytaQ6UA0wE= ------END CERTIFICATE----- diff --git a/src/python/interop/interop/credentials/server1.key b/src/python/interop/interop/credentials/server1.key deleted file mode 100755 index 143a5b8765..0000000000 --- a/src/python/interop/interop/credentials/server1.key +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD -M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf -3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY -AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm -V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY -tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p -dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q -K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR -81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff -DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd -aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 -ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 -XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe -F98XJ7tIFfJq ------END PRIVATE KEY----- diff --git a/src/python/interop/interop/credentials/server1.pem b/src/python/interop/interop/credentials/server1.pem deleted file mode 100755 index 8e582e571f..0000000000 --- a/src/python/interop/interop/credentials/server1.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET -MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ -dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 -MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV -BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl -c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs -JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO -RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 -3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL -BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 -b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ -KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS -wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e -aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= ------END CERTIFICATE----- diff --git a/src/python/interop/interop/empty_pb2.py b/src/python/interop/interop/empty_pb2.py deleted file mode 100644 index 8c1ce2f13e..0000000000 --- a/src/python/interop/interop/empty_pb2.py +++ /dev/null @@ -1,63 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/empty.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/empty.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1ctest/cpp/interop/empty.proto\x12\x0cgrpc.testing\"\x07\n\x05\x45mpty') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_EMPTY = _descriptor.Descriptor( - name='Empty', - full_name='grpc.testing.Empty', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=46, - serialized_end=53, -) - -DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY - -Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( - DESCRIPTOR = _EMPTY, - __module__ = 'test.cpp.interop.empty_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.Empty) - )) -_sym_db.RegisterMessage(Empty) - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/interop/messages_pb2.py b/src/python/interop/interop/messages_pb2.py deleted file mode 100644 index 0bf3d86a31..0000000000 --- a/src/python/interop/interop/messages_pb2.py +++ /dev/null @@ -1,447 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/messages.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/messages.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1ftest/cpp/interop/messages.proto\x12\x0cgrpc.testing\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"\xb1\x01\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"C\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"7\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\"\xb5\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload*?\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x12\x12\n\x0eUNCOMPRESSABLE\x10\x01\x12\n\n\x06RANDOM\x10\x02') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -_PAYLOADTYPE = _descriptor.EnumDescriptor( - name='PayloadType', - full_name='grpc.testing.PayloadType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='COMPRESSABLE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNCOMPRESSABLE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RANDOM', index=2, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=836, - serialized_end=899, -) -_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE) - -PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE) -COMPRESSABLE = 0 -UNCOMPRESSABLE = 1 -RANDOM = 2 - - - -_PAYLOAD = _descriptor.Descriptor( - name='Payload', - full_name='grpc.testing.Payload', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='grpc.testing.Payload.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='body', full_name='grpc.testing.Payload.body', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=49, - serialized_end=113, -) - - -_SIMPLEREQUEST = _descriptor.Descriptor( - name='SimpleRequest', - full_name='grpc.testing.SimpleRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=116, - serialized_end=293, -) - - -_SIMPLERESPONSE = _descriptor.Descriptor( - name='SimpleResponse', - full_name='grpc.testing.SimpleResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='username', full_name='grpc.testing.SimpleResponse.username', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=295, - serialized_end=390, -) - - -_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor( - name='StreamingInputCallRequest', - full_name='grpc.testing.StreamingInputCallRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=392, - serialized_end=459, -) - - -_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor( - name='StreamingInputCallResponse', - full_name='grpc.testing.StreamingInputCallResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=461, - serialized_end=522, -) - - -_RESPONSEPARAMETERS = _descriptor.Descriptor( - name='ResponseParameters', - full_name='grpc.testing.ResponseParameters', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='size', full_name='grpc.testing.ResponseParameters.size', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=524, - serialized_end=579, -) - - -_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor( - name='StreamingOutputCallRequest', - full_name='grpc.testing.StreamingOutputCallRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=582, - serialized_end=763, -) - - -_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor( - name='StreamingOutputCallResponse', - full_name='grpc.testing.StreamingOutputCallResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=765, - serialized_end=834, -) - -_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE -_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE -_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS -_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD -_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD -DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD -DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST -DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE -DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST -DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE -DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS -DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST -DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE -DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE - -Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict( - DESCRIPTOR = _PAYLOAD, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.Payload) - )) -_sym_db.RegisterMessage(Payload) - -SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict( - DESCRIPTOR = _SIMPLEREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest) - )) -_sym_db.RegisterMessage(SimpleRequest) - -SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict( - DESCRIPTOR = _SIMPLERESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse) - )) -_sym_db.RegisterMessage(SimpleResponse) - -StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGINPUTCALLREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest) - )) -_sym_db.RegisterMessage(StreamingInputCallRequest) - -StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse) - )) -_sym_db.RegisterMessage(StreamingInputCallResponse) - -ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict( - DESCRIPTOR = _RESPONSEPARAMETERS, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters) - )) -_sym_db.RegisterMessage(ResponseParameters) - -StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest) - )) -_sym_db.RegisterMessage(StreamingOutputCallRequest) - -StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE, - __module__ = 'test.cpp.interop.messages_pb2' - # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse) - )) -_sym_db.RegisterMessage(StreamingOutputCallResponse) - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/interop/methods.py b/src/python/interop/interop/methods.py deleted file mode 100644 index 194afadb17..0000000000 --- a/src/python/interop/interop/methods.py +++ /dev/null @@ -1,375 +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. - -"""Implementations of interoperability test methods.""" - -import enum -import json -import os -import threading - -from oauth2client import client as oauth2client_client - -from grpc.framework.alpha import utilities - -from interop import empty_pb2 -from interop import messages_pb2 - -_TIMEOUT = 7 - - -def _empty_call(request, unused_context): - return empty_pb2.Empty() - -_CLIENT_EMPTY_CALL = utilities.unary_unary_invocation_description( - empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString) -_SERVER_EMPTY_CALL = utilities.unary_unary_service_description( - _empty_call, empty_pb2.Empty.FromString, - empty_pb2.Empty.SerializeToString) - - -def _unary_call(request, unused_context): - return messages_pb2.SimpleResponse( - payload=messages_pb2.Payload( - type=messages_pb2.COMPRESSABLE, - body=b'\x00' * request.response_size)) - -_CLIENT_UNARY_CALL = utilities.unary_unary_invocation_description( - messages_pb2.SimpleRequest.SerializeToString, - messages_pb2.SimpleResponse.FromString) -_SERVER_UNARY_CALL = utilities.unary_unary_service_description( - _unary_call, messages_pb2.SimpleRequest.FromString, - messages_pb2.SimpleResponse.SerializeToString) - - -def _streaming_output_call(request, unused_context): - for response_parameters in request.response_parameters: - yield messages_pb2.StreamingOutputCallResponse( - payload=messages_pb2.Payload( - type=request.response_type, - body=b'\x00' * response_parameters.size)) - -_CLIENT_STREAMING_OUTPUT_CALL = utilities.unary_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_STREAMING_OUTPUT_CALL = utilities.unary_stream_service_description( - _streaming_output_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - - -def _streaming_input_call(request_iterator, unused_context): - aggregate_size = 0 - for request in request_iterator: - if request.payload and request.payload.body: - aggregate_size += len(request.payload.body) - return messages_pb2.StreamingInputCallResponse( - aggregated_payload_size=aggregate_size) - -_CLIENT_STREAMING_INPUT_CALL = utilities.stream_unary_invocation_description( - messages_pb2.StreamingInputCallRequest.SerializeToString, - messages_pb2.StreamingInputCallResponse.FromString) -_SERVER_STREAMING_INPUT_CALL = utilities.stream_unary_service_description( - _streaming_input_call, - messages_pb2.StreamingInputCallRequest.FromString, - messages_pb2.StreamingInputCallResponse.SerializeToString) - - -def _full_duplex_call(request_iterator, unused_context): - for request in request_iterator: - yield messages_pb2.StreamingOutputCallResponse( - payload=messages_pb2.Payload( - type=request.payload.type, - body=b'\x00' * request.response_parameters[0].size)) - -_CLIENT_FULL_DUPLEX_CALL = utilities.stream_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_FULL_DUPLEX_CALL = utilities.stream_stream_service_description( - _full_duplex_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - -# NOTE(nathaniel): Apparently this is the same as the full-duplex call? -_CLIENT_HALF_DUPLEX_CALL = utilities.stream_stream_invocation_description( - messages_pb2.StreamingOutputCallRequest.SerializeToString, - messages_pb2.StreamingOutputCallResponse.FromString) -_SERVER_HALF_DUPLEX_CALL = utilities.stream_stream_service_description( - _full_duplex_call, - messages_pb2.StreamingOutputCallRequest.FromString, - messages_pb2.StreamingOutputCallResponse.SerializeToString) - - -SERVICE_NAME = 'grpc.testing.TestService' - -_EMPTY_CALL_METHOD_NAME = 'EmptyCall' -_UNARY_CALL_METHOD_NAME = 'UnaryCall' -_STREAMING_OUTPUT_CALL_METHOD_NAME = 'StreamingOutputCall' -_STREAMING_INPUT_CALL_METHOD_NAME = 'StreamingInputCall' -_FULL_DUPLEX_CALL_METHOD_NAME = 'FullDuplexCall' -_HALF_DUPLEX_CALL_METHOD_NAME = 'HalfDuplexCall' - -CLIENT_METHODS = { - _EMPTY_CALL_METHOD_NAME: _CLIENT_EMPTY_CALL, - _UNARY_CALL_METHOD_NAME: _CLIENT_UNARY_CALL, - _STREAMING_OUTPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_OUTPUT_CALL, - _STREAMING_INPUT_CALL_METHOD_NAME: _CLIENT_STREAMING_INPUT_CALL, - _FULL_DUPLEX_CALL_METHOD_NAME: _CLIENT_FULL_DUPLEX_CALL, - _HALF_DUPLEX_CALL_METHOD_NAME: _CLIENT_HALF_DUPLEX_CALL, -} - -SERVER_METHODS = { - _EMPTY_CALL_METHOD_NAME: _SERVER_EMPTY_CALL, - _UNARY_CALL_METHOD_NAME: _SERVER_UNARY_CALL, - _STREAMING_OUTPUT_CALL_METHOD_NAME: _SERVER_STREAMING_OUTPUT_CALL, - _STREAMING_INPUT_CALL_METHOD_NAME: _SERVER_STREAMING_INPUT_CALL, - _FULL_DUPLEX_CALL_METHOD_NAME: _SERVER_FULL_DUPLEX_CALL, - _HALF_DUPLEX_CALL_METHOD_NAME: _SERVER_HALF_DUPLEX_CALL, -} - - -def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope): - with stub: - request = messages_pb2.SimpleRequest( - response_type=messages_pb2.COMPRESSABLE, response_size=314159, - payload=messages_pb2.Payload(body=b'\x00' * 271828), - fill_username=fill_username, fill_oauth_scope=fill_oauth_scope) - response_future = stub.UnaryCall.async(request, _TIMEOUT) - response = response_future.result() - if response.payload.type is not messages_pb2.COMPRESSABLE: - raise ValueError( - 'response payload type is "%s"!' % type(response.payload.type)) - if len(response.payload.body) != 314159: - raise ValueError( - 'response body of incorrect size %d!' % len(response.payload.body)) - return response - - -def _empty_unary(stub): - with stub: - response = stub.EmptyCall(empty_pb2.Empty(), _TIMEOUT) - if not isinstance(response, empty_pb2.Empty): - raise TypeError( - 'response is of type "%s", not empty_pb2.Empty!', type(response)) - - -def _large_unary(stub): - _large_unary_common_behavior(stub, False, False) - - -def _client_streaming(stub): - with stub: - payload_body_sizes = (27182, 8, 1828, 45904) - payloads = ( - messages_pb2.Payload(body=b'\x00' * size) - for size in payload_body_sizes) - requests = ( - messages_pb2.StreamingInputCallRequest(payload=payload) - for payload in payloads) - response = stub.StreamingInputCall(requests, _TIMEOUT) - if response.aggregated_payload_size != 74922: - raise ValueError( - 'incorrect size %d!' % response.aggregated_payload_size) - - -def _server_streaming(stub): - sizes = (31415, 9, 2653, 58979) - - with stub: - request = messages_pb2.StreamingOutputCallRequest( - response_type=messages_pb2.COMPRESSABLE, - response_parameters=( - messages_pb2.ResponseParameters(size=sizes[0]), - messages_pb2.ResponseParameters(size=sizes[1]), - messages_pb2.ResponseParameters(size=sizes[2]), - messages_pb2.ResponseParameters(size=sizes[3]), - )) - response_iterator = stub.StreamingOutputCall(request, _TIMEOUT) - for index, response in enumerate(response_iterator): - if response.payload.type != messages_pb2.COMPRESSABLE: - raise ValueError( - 'response body of invalid type %s!' % response.payload.type) - if len(response.payload.body) != sizes[index]: - raise ValueError( - 'response body of invalid size %d!' % len(response.payload.body)) - -def _cancel_after_begin(stub): - with stub: - sizes = (27182, 8, 1828, 45904) - payloads = [messages_pb2.Payload(body=b'\x00' * size) for size in sizes] - requests = [messages_pb2.StreamingInputCallRequest(payload=payload) - for payload in payloads] - responses = stub.StreamingInputCall.async(requests, _TIMEOUT) - responses.cancel() - if not responses.cancelled(): - raise ValueError('expected call to be cancelled') - - -class _Pipe(object): - - def __init__(self): - self._condition = threading.Condition() - self._values = [] - self._open = True - - def __iter__(self): - return self - - def next(self): - with self._condition: - while not self._values and self._open: - self._condition.wait() - if self._values: - return self._values.pop(0) - else: - raise StopIteration() - - def add(self, value): - with self._condition: - self._values.append(value) - self._condition.notify() - - def close(self): - with self._condition: - self._open = False - self._condition.notify() - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.close() - - -def _ping_pong(stub): - request_response_sizes = (31415, 9, 2653, 58979) - request_payload_sizes = (27182, 8, 1828, 45904) - - with stub, _Pipe() as pipe: - response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) - print 'Starting ping-pong with response iterator %s' % response_iterator - for response_size, payload_size in zip( - request_response_sizes, request_payload_sizes): - request = messages_pb2.StreamingOutputCallRequest( - response_type=messages_pb2.COMPRESSABLE, - response_parameters=(messages_pb2.ResponseParameters( - size=response_size),), - payload=messages_pb2.Payload(body=b'\x00' * payload_size)) - pipe.add(request) - response = next(response_iterator) - if response.payload.type != messages_pb2.COMPRESSABLE: - raise ValueError( - 'response body of invalid type %s!' % response.payload.type) - if len(response.payload.body) != response_size: - raise ValueError( - 'response body of invalid size %d!' % len(response.payload.body)) - - -def _cancel_after_first_response(stub): - request_response_sizes = (31415, 9, 2653, 58979) - request_payload_sizes = (27182, 8, 1828, 45904) - with stub, _Pipe() as pipe: - response_iterator = stub.FullDuplexCall(pipe, _TIMEOUT) - - response_size = request_response_sizes[0] - payload_size = request_payload_sizes[0] - request = messages_pb2.StreamingOutputCallRequest( - response_type=messages_pb2.COMPRESSABLE, - response_parameters=(messages_pb2.ResponseParameters( - size=response_size),), - payload=messages_pb2.Payload(body=b'\x00' * payload_size)) - pipe.add(request) - response = next(response_iterator) - # We test the contents of `response` in the Ping Pong test - don't check - # them here. - response_iterator.cancel() - - try: - next(response_iterator) - except Exception: - pass - else: - raise ValueError('expected call to be cancelled') - - -def _compute_engine_creds(stub, args): - response = _large_unary_common_behavior(stub, True, True) - if args.default_service_account != response.username: - raise ValueError( - 'expected username %s, got %s' % (args.default_service_account, - response.username)) - - -def _service_account_creds(stub, args): - json_key_filename = os.environ[ - oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS] - wanted_email = json.load(open(json_key_filename, 'rb'))['client_email'] - response = _large_unary_common_behavior(stub, True, True) - if wanted_email != response.username: - raise ValueError( - 'expected username %s, got %s' % (wanted_email, response.username)) - if args.oauth_scope.find(response.oauth_scope) == -1: - raise ValueError( - 'expected to find oauth scope "%s" in received "%s"' % - (response.oauth_scope, args.oauth_scope)) - - -@enum.unique -class TestCase(enum.Enum): - EMPTY_UNARY = 'empty_unary' - LARGE_UNARY = 'large_unary' - SERVER_STREAMING = 'server_streaming' - CLIENT_STREAMING = 'client_streaming' - PING_PONG = 'ping_pong' - CANCEL_AFTER_BEGIN = 'cancel_after_begin' - CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response' - COMPUTE_ENGINE_CREDS = 'compute_engine_creds' - SERVICE_ACCOUNT_CREDS = 'service_account_creds' - - def test_interoperability(self, stub, args): - if self is TestCase.EMPTY_UNARY: - _empty_unary(stub) - elif self is TestCase.LARGE_UNARY: - _large_unary(stub) - elif self is TestCase.SERVER_STREAMING: - _server_streaming(stub) - elif self is TestCase.CLIENT_STREAMING: - _client_streaming(stub) - elif self is TestCase.PING_PONG: - _ping_pong(stub) - elif self is TestCase.CANCEL_AFTER_BEGIN: - _cancel_after_begin(stub) - elif self is TestCase.CANCEL_AFTER_FIRST_RESPONSE: - _cancel_after_first_response(stub) - elif self is TestCase.COMPUTE_ENGINE_CREDS: - _compute_engine_creds(stub, args) - elif self is TestCase.SERVICE_ACCOUNT_CREDS: - _service_account_creds(stub, args) - else: - raise NotImplementedError('Test case "%s" not implemented!' % self.name) diff --git a/src/python/interop/interop/resources.py b/src/python/interop/interop/resources.py deleted file mode 100644 index 2c3045313d..0000000000 --- a/src/python/interop/interop/resources.py +++ /dev/null @@ -1,56 +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. - -"""Constants and functions for data used in interoperability testing.""" - -import os - -import pkg_resources - -_ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem' -_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' -_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' - - -def test_root_certificates(): - return pkg_resources.resource_string( - __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) - - -def prod_root_certificates(): - return open(os.environ['SSL_CERT_FILE'], mode='rb').read() - - -def private_key(): - return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) - - -def certificate_chain(): - return pkg_resources.resource_string( - __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) diff --git a/src/python/interop/interop/server.py b/src/python/interop/interop/server.py deleted file mode 100644 index a67d412038..0000000000 --- a/src/python/interop/interop/server.py +++ /dev/null @@ -1,74 +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. - -"""The Python implementation of the GRPC interoperability test server.""" - -import argparse -import logging -import time - -from grpc.early_adopter import implementations - -from interop import methods -from interop import resources - -_ONE_DAY_IN_SECONDS = 60 * 60 * 24 - - -def serve(): - parser = argparse.ArgumentParser() - parser.add_argument( - '--port', help='the port on which to serve', type=int) - parser.add_argument( - '--use_tls', help='require a secure connection', dest='use_tls', - action='store_true') - args = parser.parse_args() - - if args.use_tls: - private_key = resources.private_key() - certificate_chain = resources.certificate_chain() - server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, args.port, - private_key=private_key, certificate_chain=certificate_chain) - else: - server = implementations.server( - methods.SERVICE_NAME, methods.SERVER_METHODS, args.port) - - server.start() - logging.info('Server serving.') - try: - while True: - time.sleep(_ONE_DAY_IN_SECONDS) - except BaseException as e: - logging.info('Caught exception "%s"; stopping server...', e) - server.stop() - logging.info('Server stopped; exiting.') - -if __name__ == '__main__': - serve() diff --git a/src/python/interop/interop/test_pb2.py b/src/python/interop/interop/test_pb2.py deleted file mode 100644 index 71325d5a9f..0000000000 --- a/src/python/interop/interop/test_pb2.py +++ /dev/null @@ -1,178 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: test/cpp/interop/test.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from test.cpp.interop import empty_pb2 as test_dot_cpp_dot_interop_dot_empty__pb2 -from test.cpp.interop import messages_pb2 as test_dot_cpp_dot_interop_dot_messages__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='test/cpp/interop/test.proto', - package='grpc.testing', - serialized_pb=_b('\n\x1btest/cpp/interop/test.proto\x12\x0cgrpc.testing\x1a\x1ctest/cpp/interop/empty.proto\x1a\x1ftest/cpp/interop/messages.proto2\xbb\x04\n\x0bTestService\x12\x35\n\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12\x46\n\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n\x12StreamingInputCall\x12\'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n\x0e\x46ullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01\x12i\n\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01') - , - dependencies=[test_dot_cpp_dot_interop_dot_empty__pb2.DESCRIPTOR,test_dot_cpp_dot_interop_dot_messages__pb2.DESCRIPTOR,]) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - - -import abc -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -class EarlyAdopterTestServiceServicer(object): - """""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def EmptyCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def UnaryCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def StreamingOutputCall(self, request, context): - raise NotImplementedError() - @abc.abstractmethod - def StreamingInputCall(self, request_iterator, context): - raise NotImplementedError() - @abc.abstractmethod - def FullDuplexCall(self, request_iterator, context): - raise NotImplementedError() - @abc.abstractmethod - def HalfDuplexCall(self, request_iterator, context): - raise NotImplementedError() -class EarlyAdopterTestServiceServer(object): - """""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def start(self): - raise NotImplementedError() - @abc.abstractmethod - def stop(self): - raise NotImplementedError() -class EarlyAdopterTestServiceStub(object): - """""" - __metaclass__ = abc.ABCMeta - @abc.abstractmethod - def EmptyCall(self, request): - raise NotImplementedError() - EmptyCall.async = None - @abc.abstractmethod - def UnaryCall(self, request): - raise NotImplementedError() - UnaryCall.async = None - @abc.abstractmethod - def StreamingOutputCall(self, request): - raise NotImplementedError() - StreamingOutputCall.async = None - @abc.abstractmethod - def StreamingInputCall(self, request_iterator): - raise NotImplementedError() - StreamingInputCall.async = None - @abc.abstractmethod - def FullDuplexCall(self, request_iterator): - raise NotImplementedError() - FullDuplexCall.async = None - @abc.abstractmethod - def HalfDuplexCall(self, request_iterator): - raise NotImplementedError() - HalfDuplexCall.async = None -def early_adopter_create_TestService_server(servicer, port, private_key=None, certificate_chain=None): - import test.cpp.interop.empty_pb2 - import test.cpp.interop.empty_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - method_service_descriptions = { - "EmptyCall": utilities.unary_unary_service_description( - servicer.EmptyCall, - test.cpp.interop.empty_pb2.Empty.FromString, - test.cpp.interop.empty_pb2.Empty.SerializeToString, - ), - "FullDuplexCall": utilities.stream_stream_service_description( - servicer.FullDuplexCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "HalfDuplexCall": utilities.stream_stream_service_description( - servicer.HalfDuplexCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "StreamingInputCall": utilities.stream_unary_service_description( - servicer.StreamingInputCall, - test.cpp.interop.messages_pb2.StreamingInputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingInputCallResponse.SerializeToString, - ), - "StreamingOutputCall": utilities.unary_stream_service_description( - servicer.StreamingOutputCall, - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.FromString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.SerializeToString, - ), - "UnaryCall": utilities.unary_unary_service_description( - servicer.UnaryCall, - test.cpp.interop.messages_pb2.SimpleRequest.FromString, - test.cpp.interop.messages_pb2.SimpleResponse.SerializeToString, - ), - } - return implementations.server("grpc.testing.TestService", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) -def early_adopter_create_TestService_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): - import test.cpp.interop.empty_pb2 - import test.cpp.interop.empty_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - import test.cpp.interop.messages_pb2 - method_invocation_descriptions = { - "EmptyCall": utilities.unary_unary_invocation_description( - test.cpp.interop.empty_pb2.Empty.SerializeToString, - test.cpp.interop.empty_pb2.Empty.FromString, - ), - "FullDuplexCall": utilities.stream_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "HalfDuplexCall": utilities.stream_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "StreamingInputCall": utilities.stream_unary_invocation_description( - test.cpp.interop.messages_pb2.StreamingInputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingInputCallResponse.FromString, - ), - "StreamingOutputCall": utilities.unary_stream_invocation_description( - test.cpp.interop.messages_pb2.StreamingOutputCallRequest.SerializeToString, - test.cpp.interop.messages_pb2.StreamingOutputCallResponse.FromString, - ), - "UnaryCall": utilities.unary_unary_invocation_description( - test.cpp.interop.messages_pb2.SimpleRequest.SerializeToString, - test.cpp.interop.messages_pb2.SimpleResponse.FromString, - ), - } - return implementations.stub("grpc.testing.TestService", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) -# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/setup.py b/src/python/interop/setup.py deleted file mode 100644 index 75012b0d8f..0000000000 --- a/src/python/interop/setup.py +++ /dev/null @@ -1,57 +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. - -"""A setup module for the GRPC Python interop testing package.""" - -import setuptools - -_PACKAGES = ( - 'interop', -) - -_PACKAGE_DIRECTORIES = { - 'interop': 'interop', -} - -_PACKAGE_DATA = { - 'interop': [ - 'credentials/ca.pem', 'credentials/server1.key', - 'credentials/server1.pem',] -} - -_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.10.0a0'] - -setuptools.setup( - name='interop', - version='0.0.1', - packages=_PACKAGES, - package_dir=_PACKAGE_DIRECTORIES, - package_data=_PACKAGE_DATA, - install_requires=_INSTALL_REQUIRES -) diff --git a/src/python/src/.gitignore b/src/python/src/.gitignore deleted file mode 100644 index d89f3db999..0000000000 --- a/src/python/src/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -MANIFEST -grpcio.egg-info/ -build/ -dist/ -*.egg -*.egg/ -*.eggs/ -doc/ diff --git a/src/python/src/MANIFEST.in b/src/python/src/MANIFEST.in deleted file mode 100644 index 498b55f20a..0000000000 --- a/src/python/src/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -graft grpc -include commands.py diff --git a/src/python/src/README.rst b/src/python/src/README.rst deleted file mode 100644 index 00bdecf56f..0000000000 --- a/src/python/src/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -gRPC Python -=========== - -Package for GRPC Python. - -Dependencies ------------- - -Ensure you have installed the gRPC core. On Mac OS X, install homebrew_. On Linux, install linuxbrew_. -Run the following command to install gRPC Python. - -:: - - $ curl -fsSL https://goo.gl/getgrpc | bash -s python - -This will download and run the [gRPC install script][] to install grpc core. The script then uses pip to install this package. It also installs the Protocol Buffers compiler (_protoc_) and the gRPC _protoc_ plugin for python. - -Otherwise, `install from source`_ - -.. _`install from source`: https://github.com/grpc/grpc/blob/master/src/python/README.md#building-from-source -.. _homebrew: http://brew.sh -.. _linuxbrew: https://github.com/Homebrew/linuxbrew#installation -.. _`gRPC install script`: https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install diff --git a/src/python/src/commands.py b/src/python/src/commands.py deleted file mode 100644 index 8e87855011..0000000000 --- a/src/python/src/commands.py +++ /dev/null @@ -1,75 +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. - -"""Provides distutils command classes for the GRPC Python setup process.""" - -import os -import os.path -import sys - -import setuptools - -_CONF_PY_ADDENDUM = """ -extensions.append('sphinx.ext.napoleon') -napoleon_google_docstring = True -napoleon_numpy_docstring = True - -html_theme = 'sphinx_rtd_theme' -""" - -class SphinxDocumentation(setuptools.Command): - """Command to generate documentation via sphinx.""" - - description = '' - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - # We import here to ensure that setup.py has had a chance to install the - # relevant package eggs first. - import sphinx - import sphinx.apidoc - metadata = self.distribution.metadata - src_dir = os.path.join( - os.getcwd(), self.distribution.package_dir['grpc']) - sys.path.append(src_dir) - sphinx.apidoc.main([ - '', '--force', '--full', '-H', metadata.name, '-A', metadata.author, - '-V', metadata.version, '-R', metadata.version, - '-o', os.path.join('doc', 'src'), src_dir]) - conf_filepath = os.path.join('doc', 'src', 'conf.py') - with open(conf_filepath, 'a') as conf_file: - conf_file.write(_CONF_PY_ADDENDUM) - sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) - diff --git a/src/python/src/grpc/__init__.py b/src/python/src/grpc/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/_adapter/.gitignore b/src/python/src/grpc/_adapter/.gitignore deleted file mode 100644 index a6f96cd6db..0000000000 --- a/src/python/src/grpc/_adapter/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.a -*.so -*.dll -*.pyc -*.pyd diff --git a/src/python/src/grpc/_adapter/__init__.py b/src/python/src/grpc/_adapter/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/_adapter/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/_adapter/_blocking_invocation_inline_service_test.py b/src/python/src/grpc/_adapter/_blocking_invocation_inline_service_test.py deleted file mode 100644 index 7a8ff0ad89..0000000000 --- a/src/python/src/grpc/_adapter/_blocking_invocation_inline_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case - - -class BlockingInvocationInlineServiceTest( - _face_test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_c/module.c b/src/python/src/grpc/_adapter/_c/module.c deleted file mode 100644 index 1f3aedd9d8..0000000000 --- a/src/python/src/grpc/_adapter/_c/module.c +++ /dev/null @@ -1,61 +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 - -#define PY_SSIZE_T_CLEAN -#include -#include - -#include "grpc/_adapter/_c/types.h" - -static PyMethodDef c_methods[] = { - {NULL} -}; - -PyMODINIT_FUNC init_c(void) { - PyObject *module; - - module = Py_InitModule3("_c", c_methods, - "Wrappings of C structures and functions."); - - 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); -} diff --git a/src/python/src/grpc/_adapter/_c/types.c b/src/python/src/grpc/_adapter/_c/types.c deleted file mode 100644 index 8855c32ca6..0000000000 --- a/src/python/src/grpc/_adapter/_c/types.c +++ /dev/null @@ -1,60 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include - -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; -} diff --git a/src/python/src/grpc/_adapter/_c/types.h b/src/python/src/grpc/_adapter/_c/types.h deleted file mode 100644 index 4e0da4a28a..0000000000 --- a/src/python/src/grpc/_adapter/_c/types.h +++ /dev/null @@ -1,267 +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 GRPC__ADAPTER__C_TYPES_H_ -#define GRPC__ADAPTER__C_TYPES_H_ - -#define PY_SSIZE_T_CLEAN -#include -#include -#include - - -/*=========================*/ -/* 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_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); -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_pyseq_to_send_metadata( - PyObject *pyseq, grpc_metadata **metadata, size_t *count); -/* Returns a metadata array as a Python object on success, else NULL. */ -PyObject *pygrpc_cast_metadata_array_to_pyseq(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_ */ diff --git a/src/python/src/grpc/_adapter/_c/types/call.c b/src/python/src/grpc/_adapter/_c/types/call.c deleted file mode 100644 index 0739070044..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/call.c +++ /dev/null @@ -1,163 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include -#include - - -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); -} diff --git a/src/python/src/grpc/_adapter/_c/types/channel.c b/src/python/src/grpc/_adapter/_c/types/channel.c deleted file mode 100644 index feb256cf00..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/channel.c +++ /dev/null @@ -1,134 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include - - -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_insecure_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; -} diff --git a/src/python/src/grpc/_adapter/_c/types/client_credentials.c b/src/python/src/grpc/_adapter/_c/types/client_credentials.c deleted file mode 100644 index e314c15324..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/client_credentials.c +++ /dev/null @@ -1,270 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include -#include - - -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, ""}, - {"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; -} - -/* TODO: Rename this credentials to something like service_account_jwt_access */ -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_service_account_jwt_access_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_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; -} - diff --git a/src/python/src/grpc/_adapter/_c/types/completion_queue.c b/src/python/src/grpc/_adapter/_c/types/completion_queue.c deleted file mode 100644 index 2dd44b6ddd..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/completion_queue.c +++ /dev/null @@ -1,124 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include - - -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; -} diff --git a/src/python/src/grpc/_adapter/_c/types/server.c b/src/python/src/grpc/_adapter/_c/types/server.c deleted file mode 100644 index 2a00f34039..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/server.c +++ /dev/null @@ -1,180 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include - - -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); - grpc_server_register_completion_queue(self->c_serv, cq->c_cq); - 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; - pygrpc_tag *tag; - static char *keywords[] = {"tag", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) { - return NULL; - } - tag = pygrpc_produce_server_shutdown_tag(user_tag); - grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag); - Py_RETURN_NONE; -} diff --git a/src/python/src/grpc/_adapter/_c/types/server_credentials.c b/src/python/src/grpc/_adapter/_c/types/server_credentials.c deleted file mode 100644 index f6859b79d7..0000000000 --- a/src/python/src/grpc/_adapter/_c/types/server_credentials.c +++ /dev/null @@ -1,138 +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/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - - -PyMethodDef pygrpc_ServerCredentials_methods[] = { - {"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl, - METH_CLASS|METH_KEYWORDS, ""}, - {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); - /* TODO: Add a force_client_auth parameter in the python object and pass it - here as the last arg. */ - self->c_creds = grpc_ssl_server_credentials_create( - root_certs, key_cert_pairs, num_key_cert_pairs, 0); - gpr_free(key_cert_pairs); - return self; -} - diff --git a/src/python/src/grpc/_adapter/_c/utility.c b/src/python/src/grpc/_adapter/_c/utility.c deleted file mode 100644 index 51f3c9be01..0000000000 --- a/src/python/src/grpc/_adapter/_c/utility.c +++ /dev/null @@ -1,506 +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 -#include - -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include -#include -#include -#include - -#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_pyseq(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 ? (PyObject*)tag->call : Py_None, 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; - int type; - Py_ssize_t message_size; - char *message; - char *status_details; - gpr_slice message_slice; - 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; - gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE); - PyErr_SetString(PyExc_ValueError, buf); - gpr_free(buf); - return 0; - } - type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); - if (PyErr_Occurred()) { - return 0; - } - c_op.op = type; - c_op.flags = 0; - switch (type) { - case GRPC_OP_SEND_INITIAL_METADATA: - if (!pygrpc_cast_pyseq_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_raw_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_pyseq_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; - gpr_asprintf(&buf, "expected tuple status in op of length %d", - STATUS_TUPLE_SIZE); - PyErr_SetString(PyExc_ValueError, buf); - gpr_free(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) { - size_t i; - switch(op.op) { - case GRPC_OP_SEND_INITIAL_METADATA: - /* Whenever we produce send-metadata, we allocate new strings (to handle - arbitrary sequence input as opposed to just lists or just tuples). We - thus must free those elements. */ - for (i = 0; i < op.data.send_initial_metadata.count; ++i) { - gpr_free((void *)op.data.send_initial_metadata.metadata[i].key); - gpr_free((void *)op.data.send_initial_metadata.metadata[i].value); - } - 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: - /* Whenever we produce send-metadata, we allocate new strings (to handle - arbitrary sequence input as opposed to just lists or just tuples). We - thus must free those elements. */ - for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count; - ++i) { - gpr_free( - (void *)op.data.send_status_from_server.trailing_metadata[i].key); - gpr_free( - (void *)op.data.send_status_from_server.trailing_metadata[i].value); - } - 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) { - timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); - return timespec.tv_sec + 1e-9*timespec.tv_nsec; -} - -/* Because C89 doesn't have a way to check for infinity... */ -static int pygrpc_isinf(double x) { - return x * 0 != 0; -} - -gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { - gpr_timespec result; - if (pygrpc_isinf(seconds)) { - result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) - : gpr_inf_past(GPR_CLOCK_REALTIME); - } else { - result.tv_sec = (time_t)seconds; - result.tv_nsec = ((seconds - result.tv_sec) * 1e9); - result.clock_type = GPR_CLOCK_REALTIME; - } - 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; - args.num_args = num_args; - args.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_pyseq_to_send_metadata( - PyObject *pyseq, grpc_metadata **metadata, size_t *count) { - size_t i; - Py_ssize_t value_length; - char *key; - char *value; - if (!PySequence_Check(pyseq)) { - return 0; - } - *count = PySequence_Size(pyseq); - *metadata = gpr_malloc(sizeof(grpc_metadata) * *count); - for (i = 0; i < *count; ++i) { - PyObject *item = PySequence_GetItem(pyseq, i); - if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) { - Py_DECREF(item); - gpr_free(*metadata); - *count = 0; - *metadata = NULL; - return 0; - } else { - (*metadata)[i].key = gpr_strdup(key); - (*metadata)[i].value = gpr_malloc(value_length); - memcpy((void *)(*metadata)[i].value, value, value_length); - Py_DECREF(item); - } - (*metadata)[i].value_length = value_length; - } - return 1; -} - -PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { - PyObject *result = PyTuple_New(metadata.count); - size_t i; - for (i = 0; i < metadata.count; ++i) { - PyTuple_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; - gpr_slice slice; - char *read_result = NULL; - size_t size = 0; - grpc_byte_buffer_reader_init(&reader, buffer); - 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); - } - *result_size = size; - *result = read_result; -} diff --git a/src/python/src/grpc/_adapter/_c_test.py b/src/python/src/grpc/_adapter/_c_test.py deleted file mode 100644 index fe020e2a9c..0000000000 --- a/src/python/src/grpc/_adapter/_c_test.py +++ /dev/null @@ -1,55 +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. - -import time -import unittest - -from grpc._adapter import _c -from grpc._adapter import _types - - -class CTypeSmokeTest(unittest.TestCase): - - def testCompletionQueueUpDown(self): - completion_queue = _c.CompletionQueue() - del completion_queue - - def testServerUpDown(self): - completion_queue = _c.CompletionQueue() - serv = _c.Server(completion_queue, []) - del serv - del completion_queue - - def testChannelUpDown(self): - channel = _c.Channel('[::]:0', []) - del channel - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_common.py b/src/python/src/grpc/_adapter/_common.py deleted file mode 100644 index 492849f4cb..0000000000 --- a/src/python/src/grpc/_adapter/_common.py +++ /dev/null @@ -1,76 +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. - -"""State used by both invocation-side and service-side code.""" - -import enum - - -@enum.unique -class HighWrite(enum.Enum): - """The possible categories of high-level write state.""" - - OPEN = 'OPEN' - CLOSED = 'CLOSED' - - -class WriteState(object): - """A description of the state of writing to an RPC. - - Attributes: - low: A side-specific value describing the low-level state of writing. - high: A HighWrite value describing the high-level state of writing. - pending: A list of bytestrings for the RPC waiting to be written to the - other side of the RPC. - """ - - def __init__(self, low, high, pending): - self.low = low - self.high = high - self.pending = pending - - -class CommonRPCState(object): - """A description of an RPC's state. - - Attributes: - write: A WriteState describing the state of writing to the RPC. - sequence_number: The lowest-unused sequence number for use in generating - tickets locally describing the progress of the RPC. - deserializer: The behavior to be used to deserialize payload bytestreams - taken off the wire. - serializer: The behavior to be used to serialize payloads to be sent on the - wire. - """ - - def __init__(self, write, sequence_number, deserializer, serializer): - self.write = write - self.sequence_number = sequence_number - self.deserializer = deserializer - self.serializer = serializer diff --git a/src/python/src/grpc/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/src/grpc/_adapter/_event_invocation_synchronous_event_service_test.py deleted file mode 100644 index b8ceb75d68..0000000000 --- a/src/python/src/grpc/_adapter/_event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_face_test_case.py b/src/python/src/grpc/_adapter/_face_test_case.py deleted file mode 100644 index 5fa974ed06..0000000000 --- a/src/python/src/grpc/_adapter/_face_test_case.py +++ /dev/null @@ -1,106 +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. - -"""Common construction and destruction for GRPC-backed Face-layer tests.""" - -import unittest - -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import util -from grpc.framework.base import implementations as base_implementations -from grpc.framework.face import implementations as face_implementations -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import serial -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_TIMEOUT = 90 -_MAXIMUM_POOL_SIZE = 4 - - -class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): - """Provides abstract Face-layer tests a GRPC-backed implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = face_implementations.servicer( - pool, method_implementations, multi_method_implementation) - - serialization = serial.serialization(methods) - - fore_link = fore.ForeLink( - pool, serialization.request_deserializers, - serialization.response_serializers, None, ()) - fore_link.start() - port = fore_link.port() - rear_link = rear.RearLink( - 'localhost', port, pool, - serialization.request_serializers, - serialization.response_deserializers, False, None, None, None) - rear_link.start() - front = base_implementations.front_link(pool, pool, pool) - back = base_implementations.back_link( - servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) - fore_link.join_rear_link(back) - back.join_fore_link(fore_link) - rear_link.join_fore_link(front) - front.join_rear_link(rear_link) - - stub = face_implementations.generic_stub(front, pool) - return stub, (rear_link, fore_link, front, back) - - def tear_down_implementation(self, memo): - rear_link, fore_link, front, back = memo - # TODO(nathaniel): Waiting for the front and back to idle possibly should - # not be necessary - investigate as part of graceful shutdown work. - util.wait_for_idle(front) - util.wait_for_idle(back) - rear_link.stop() - fore_link.stop() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/src/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/src/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index 3773e65575..0000000000 --- a/src/python/src/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_intermediary_low.py b/src/python/src/grpc/_adapter/_intermediary_low.py deleted file mode 100644 index 3c7f0a2619..0000000000 --- a/src/python/src/grpc/_adapter/_intermediary_low.py +++ /dev/null @@ -1,259 +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. - -"""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): - result = self._internal.start_batch([ - _types.OpArgs.send_initial_metadata(self._metadata) - ], _IGNORE_ME_TAG) - self._metadata = [] - return result - - 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 len(ev.results) > 0 and 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(_TagAdapter(None, Event.Kind.STOP)) - - -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)) diff --git a/src/python/src/grpc/_adapter/_intermediary_low_test.py b/src/python/src/grpc/_adapter/_intermediary_low_test.py deleted file mode 100644 index 27a5b82e9c..0000000000 --- a/src/python/src/grpc/_adapter/_intermediary_low_test.py +++ /dev/null @@ -1,434 +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. - -"""Tests for the old '_low'.""" - -import Queue -import threading -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 - - -def _drive_completion_queue(completion_queue, event_queue): - while True: - event = completion_queue.get(_FUTURE) - if event.kind is _low.Event.Kind.STOP: - break - event_queue.put(event) - - -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.server_events = Queue.Queue() - self.server_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.server_completion_queue, self.server_events)) - self.server_completion_queue_thread.start() - - self.client_completion_queue = _low.CompletionQueue() - self.channel = _low.Channel('%s:%d' % (self.host, port), None) - self.client_events = Queue.Queue() - self.client_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.client_completion_queue, self.client_events)) - self.client_completion_queue_thread.start() - - def tearDown(self): - self.server.stop() - self.server_completion_queue.stop() - self.client_completion_queue.stop() - self.server_completion_queue_thread.join() - self.client_completion_queue_thread.join() - del self.server - - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - server_terminal_event_two = self.server_events.get() - 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_events.get() - client_terminal_event_two = self.client_events.get() - 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]) - self.assertSetEqual(set(key for key, _ in finish_accepted.metadata), - set((server_trailing_metadata_key, - 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.server_events = Queue.Queue() - self.server_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.server_completion_queue, self.server_events)) - self.server_completion_queue_thread.start() - - self.client_completion_queue = _low.CompletionQueue() - self.channel = _low.Channel('%s:%d' % (self.host, port), None) - self.client_events = Queue.Queue() - self.client_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.client_completion_queue, self.client_events)) - self.client_completion_queue_thread.start() - - def tearDown(self): - self.server.stop() - self.server_completion_queue.stop() - self.client_completion_queue.stop() - self.server_completion_queue_thread.join() - self.client_completion_queue_thread.join() - del self.server - - 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_events.get() - server_call = service_accepted.service_acceptance.call - - server_call.accept(self.server_completion_queue, finish_tag) - server_call.premetadata() - - metadata_accepted = self.client_events.get() - self.assertIsNotNone(metadata_accepted) - - for datum in test_data: - client_call.write(datum, write_tag) - write_accepted = self.client_events.get() - - server_call.read(read_tag) - read_accepted = self.server_events.get() - server_data.append(read_accepted.bytes) - - server_call.write(read_accepted.bytes, write_tag) - write_accepted = self.server_events.get() - self.assertIsNotNone(write_accepted) - - client_call.read(read_tag) - read_accepted = self.client_events.get() - 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_events.get() - server_terminal_event_two = self.server_events.get() - 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_events.get() - 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) - diff --git a/src/python/src/grpc/_adapter/_links_test.py b/src/python/src/grpc/_adapter/_links_test.py deleted file mode 100644 index 4729b84f84..0000000000 --- a/src/python/src/grpc/_adapter/_links_test.py +++ /dev/null @@ -1,277 +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. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import threading -import unittest - -from grpc._adapter import _proto_scenarios -from grpc._adapter import _test_links -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool - -_IDENTITY = lambda x: x -_TIMEOUT = 32 - - -# TODO(nathaniel): End-to-end metadata testing. -def _transform_metadata(unused_metadata): - return ( - ('one unused key', 'one unused value'), - ('another unused key', 'another unused value'), -) - - -class RoundTripTest(unittest.TestCase): - - def setUp(self): - self.fore_link_pool = logging_pool.pool(8) - self.rear_link_pool = logging_pool.pool(8) - - def tearDown(self): - self.rear_link_pool.shutdown(wait=True) - self.fore_link_pool.shutdown(wait=True) - - def testZeroMessageRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_fore_link = _test_links.ForeLink(None, None) - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE): - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, 0, - interfaces.BackToFrontTicket.Kind.COMPLETION, None) - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: None}, - {test_method: None}, False, None, None, None, - metadata_transformer=_transform_metadata) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is - interfaces.BackToFrontTicket.Kind.CONTINUATION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_fore_link.condition: - self.assertIs( - test_fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testEntireRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_front_to_back_datum = b'\x07' - test_back_to_front_datum = b'\x08' - test_fore_link = _test_links.ForeLink(None, None) - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.payload is None: - payload = None - else: - payload = test_back_to_front_datum - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if payload is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - payload) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, - test_front_to_back_datum, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - front_to_back_payloads = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - back_to_front_payloads = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) - self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) - - def _perform_scenario_test(self, scenario): - test_operation_id = object() - test_method = scenario.method() - test_fore_link = _test_links.ForeLink(None, None) - rear_lock = threading.Lock() - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - with rear_lock: - if front_to_back_ticket.payload is not None: - response = scenario.response_for_request(front_to_back_ticket.payload) - else: - response = None - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if response is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - response) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: scenario.deserialize_request}, - {test_method: scenario.serialize_response}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, - {test_method: scenario.serialize_request}, - {test_method: scenario.deserialize_response}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - commencement_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, - interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - fore_sequence_number = 1 - rear_link.accept_front_to_back_ticket(commencement_ticket) - for request in scenario.requests(): - continuation_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, - request, None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(continuation_ticket) - completion_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, - None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(completion_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - requests = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - responses = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTrue(scenario.verify_requests(requests)) - self.assertTrue(scenario.verify_responses(responses)) - - def testEmptyScenario(self): - self._perform_scenario_test(_proto_scenarios.EmptyScenario()) - - def testBidirectionallyUnaryScenario(self): - self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) - - def testBidirectionallyStreamingScenario(self): - self._perform_scenario_test( - _proto_scenarios.BidirectionallyStreamingScenario()) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_lonely_rear_link_test.py b/src/python/src/grpc/_adapter/_lonely_rear_link_test.py deleted file mode 100644 index 7f5021f40e..0000000000 --- a/src/python/src/grpc/_adapter/_lonely_rear_link_test.py +++ /dev/null @@ -1,100 +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. - -"""A test of invocation-side code unconnected to an RPC server.""" - -import unittest - -from grpc._adapter import _test_links -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool - -_IDENTITY = lambda x: x -_TIMEOUT = 2 - - -class LonelyRearLinkTest(unittest.TestCase): - - def setUp(self): - self.pool = logging_pool.pool(8) - - def tearDown(self): - self.pool.shutdown(wait=True) - - def testUpAndDown(self): - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) - - rear_link.start() - rear_link.stop() - - def _perform_lonely_client_test_with_ticket_kind( - self, front_to_back_ticket_kind): - test_operation_id = object() - test_method = 'test method' - fore_link = _test_links.ForeLink(None, None) - - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {test_method: None}, - {test_method: None}, False, None, None, None) - rear_link.join_fore_link(fore_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, front_to_back_ticket_kind, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with fore_link.condition: - while True: - if (fore_link.tickets and - fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.CONTINUATION): - break - fore_link.condition.wait() - - rear_link.stop() - - with fore_link.condition: - self.assertIsNot( - fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testLonelyClientCommencementTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.COMMENCEMENT) - - def testLonelyClientEntireTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.ENTIRE) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_low.py b/src/python/src/grpc/_adapter/_low.py deleted file mode 100644 index dcf67dbc11..0000000000 --- a/src/python/src/grpc/_adapter/_low.py +++ /dev/null @@ -1,108 +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. - -from grpc._adapter import _c -from grpc._adapter import _types - -ClientCredentials = _c.ClientCredentials -ServerCredentials = _c.ServerCredentials - - -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=None): - return self.server.shutdown(tag) - - def request_call(self, completion_queue, tag): - return self.server.request_call(completion_queue.completion_queue, tag) diff --git a/src/python/src/grpc/_adapter/_low_test.py b/src/python/src/grpc/_adapter/_low_test.py deleted file mode 100644 index 9a8edfad0c..0000000000 --- a/src/python/src/grpc/_adapter/_low_test.py +++ /dev/null @@ -1,199 +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. - -import threading -import time -import unittest - -from grpc._adapter import _types -from grpc._adapter import _low - - -def WaitForEvents(completion_queues, deadline): - """ - Args: - completion_queues: list of completion queues to wait for events on - deadline: absolute deadline to wait until - - Returns: - a sequence of events of length len(completion_queues). - """ - - results = [None] * len(completion_queues) - lock = threading.Lock() - threads = [] - def set_ith_result(i, completion_queue): - result = completion_queue.next(deadline) - with lock: - print i, completion_queue, result, time.time() - deadline - results[i] = result - for i, completion_queue in enumerate(completion_queues): - thread = threading.Thread(target=set_ith_result, - args=[i, completion_queue]) - thread.start() - threads.append(thread) - for thread in threads: - thread.join() - return results - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue, []) - self.port = self.server.add_http2_port('[::]:0') - self.client_completion_queue = _low.CompletionQueue() - self.client_channel = _low.Channel('localhost:%d'%self.port, []) - - self.server.start() - - def tearDown(self): - self.server.shutdown() - del self.client_channel - - 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 - del self.server - - 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) - - client_no_event, request_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 2) - self.assertEquals(client_no_event, None) - 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)) - got_initial_metadata = dict(request_event.results[0].initial_metadata) - self.assertEquals( - dict(client_initial_metadata), - dict((x, got_initial_metadata[x]) for x in zip(*client_initial_metadata)[0])) - 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, server_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 1) - - 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__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_adapter/_proto_scenarios.py b/src/python/src/grpc/_adapter/_proto_scenarios.py deleted file mode 100644 index 60a622ba8b..0000000000 --- a/src/python/src/grpc/_adapter/_proto_scenarios.py +++ /dev/null @@ -1,261 +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. - -"""Test scenarios using protocol buffers.""" - -import abc -import threading - -from grpc._junkdrawer import math_pb2 - - -class ProtoScenario(object): - """An RPC test scenario using protocol buffers.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def method(self): - """Access the test method name. - - Returns: - The test method name. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize a request protocol buffer. - - Args: - request: A request protocol buffer. - - Returns: - The bytestring serialization of the given request protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, request_bytestring): - """Deserialize a request protocol buffer. - - Args: - request_bytestring: The bytestring serialization of a request protocol - buffer. - - Returns: - The request protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize a response protocol buffer. - - Args: - response: A response protocol buffer. - - Returns: - The bytestring serialization of the given response protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, response_bytestring): - """Deserialize a response protocol buffer. - - Args: - response_bytestring: The bytestring serialization of a response protocol - buffer. - - Returns: - The response protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def requests(self): - """Access the sequence of requests for this scenario. - - Returns: - A sequence of request protocol buffers. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_for_request(self, request): - """Access the response for a particular request. - - Args: - request: A request protocol buffer. - - Returns: - The response protocol buffer appropriate for the given request. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_requests(self, experimental_requests): - """Verify the requests transmitted through the system under test. - - Args: - experimental_requests: The request protocol buffers transmitted through - the system under test. - - Returns: - True if the requests satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_responses(self, experimental_responses): - """Verify the responses transmitted through the system under test. - - Args: - experimental_responses: The response protocol buffers transmitted through - the system under test. - - Returns: - True if the responses satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - -class EmptyScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - def method(self): - return 'DivMany' - - def serialize_request(self, request): - raise ValueError('This should not be necessary to call!') - - def deserialize_request(self, request_bytestring): - raise ValueError('This should not be necessary to call!') - - def serialize_response(self, response): - raise ValueError('This should not be necessary to call!') - - def deserialize_response(self, response_bytestring): - raise ValueError('This should not be necessary to call!') - - def requests(self): - return () - - def response_for_request(self, request): - raise ValueError('This should not be necessary to call!') - - def verify_requests(self, experimental_requests): - return not experimental_requests - - def verify_responses(self, experimental_responses): - return not experimental_responses - - -class BidirectionallyUnaryScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _DIVIDEND = 59 - _DIVISOR = 7 - _QUOTIENT = 8 - _REMAINDER = 3 - - _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) - _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) - - def method(self): - return 'Div' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return [self._REQUEST] - - def response_for_request(self, request): - return self._RESPONSE - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == (self._REQUEST,) - - def verify_responses(self, experimental_responses): - return tuple(experimental_responses) == (self._RESPONSE,) - - -class BidirectionallyStreamingScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _STREAM_LENGTH = 200 - _REQUESTS = tuple( - math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) - for index in range(_STREAM_LENGTH)) - - def __init__(self): - self._lock = threading.Lock() - self._responses = [] - - def method(self): - return 'DivMany' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return self._REQUESTS - - def response_for_request(self, request): - quotient, remainder = divmod(request.dividend, request.divisor) - response = math_pb2.DivReply(quotient=quotient, remainder=remainder) - with self._lock: - self._responses.append(response) - return response - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == self._REQUESTS - - def verify_responses(self, experimental_responses): - with self._lock: - return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/src/grpc/_adapter/_test_links.py b/src/python/src/grpc/_adapter/_test_links.py deleted file mode 100644 index 86c7e61b17..0000000000 --- a/src/python/src/grpc/_adapter/_test_links.py +++ /dev/null @@ -1,80 +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. - -"""Links suitable for use in tests.""" - -import threading - -from grpc.framework.base import interfaces - - -class ForeLink(interfaces.ForeLink): - """A ForeLink suitable for use in tests of RearLinks.""" - - def __init__(self, action, rear_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.rear_link = rear_link - - def accept_back_to_front_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, rear_link = self.action, self.rear_link - - if action is not None: - action(ticket, rear_link) - - def join_rear_link(self, rear_link): - with self.condition: - self.rear_link = rear_link - - -class RearLink(interfaces.RearLink): - """A RearLink suitable for use in tests of ForeLinks.""" - - def __init__(self, action, fore_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.fore_link = fore_link - - def accept_front_to_back_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, fore_link = self.action, self.fore_link - - if action is not None: - action(ticket, fore_link) - - def join_fore_link(self, fore_link): - with self.condition: - self.fore_link = fore_link diff --git a/src/python/src/grpc/_adapter/_types.py b/src/python/src/grpc/_adapter/_types.py deleted file mode 100644 index 5ddb1774ea..0000000000 --- a/src/python/src/grpc/_adapter/_types.py +++ /dev/null @@ -1,368 +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. - -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) ... - """ diff --git a/src/python/src/grpc/_adapter/fore.py b/src/python/src/grpc/_adapter/fore.py deleted file mode 100644 index 7d88bda263..0000000000 --- a/src/python/src/grpc/_adapter/fore.py +++ /dev/null @@ -1,363 +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. - -"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" - -import enum -import logging -import threading -import time - -from grpc._adapter import _common -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 -from grpc.framework.foundation import logging_pool - -_THREAD_POOL_SIZE = 10 - - -@enum.unique -class _LowWrite(enum.Enum): - """The possible categories of low-level write state.""" - - OPEN = 'OPEN' - ACTIVE = 'ACTIVE' - CLOSED = 'CLOSED' - - -def _write(call, rpc_state, payload): - serialized_payload = rpc_state.serializer(payload) - if rpc_state.write.low is _LowWrite.OPEN: - call.write(serialized_payload, call) - rpc_state.write.low = _LowWrite.ACTIVE - else: - rpc_state.write.pending.append(serialized_payload) - - -def _status(call, rpc_state): - call.status(_low.Status(_low.Code.OK, ''), call) - rpc_state.write.low = _LowWrite.CLOSED - - -class ForeLink(base_interfaces.ForeLink, activated.Activated): - """A service-side bridge between RPC Framework and the C-ish _low code.""" - - def __init__( - self, pool, request_deserializers, response_serializers, - root_certificates, key_chain_pairs, port=None): - """Constructor. - - Args: - pool: A thread pool. - request_deserializers: A dict from RPC method names to request object - deserializer behaviors. - response_serializers: A dict from RPC method names to response object - serializer behaviors. - root_certificates: The PEM-encoded client root certificates as a - bytestring or None. - key_chain_pairs: A sequence of PEM-encoded private key-certificate chain - pairs. - port: The port on which to serve, or None to have a port selected - automatically. - """ - self._condition = threading.Condition() - self._pool = pool - self._request_deserializers = request_deserializers - self._response_serializers = response_serializers - self._root_certificates = root_certificates - self._key_chain_pairs = key_chain_pairs - self._requested_port = port - - self._rear_link = null.NULL_REAR_LINK - self._completion_queue = None - self._server = None - self._rpc_states = {} - self._spinning = False - self._port = None - - def _on_stop_event(self): - self._spinning = False - self._condition.notify_all() - - def _on_service_acceptance_event(self, event, server): - """Handle a service invocation event.""" - service_acceptance = event.service_acceptance - if service_acceptance is None: - return - - call = service_acceptance.call - call.accept(self._completion_queue, call) - # TODO(nathaniel): Metadata support. - call.premetadata() - call.read(call) - method = service_acceptance.method - - self._rpc_states[call] = _common.CommonRPCState( - _common.WriteState(_LowWrite.OPEN, _common.HighWrite.OPEN, []), 1, - self._request_deserializers[method], - self._response_serializers[method]) - - ticket = base_interfaces.FrontToBackTicket( - call, 0, base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT, method, - base_interfaces.ServicedSubscription.Kind.FULL, None, None, - service_acceptance.deadline - time.time()) - self._rear_link.accept_front_to_back_ticket(ticket) - - server.service(None) - - def _on_read_event(self, event): - """Handle data arriving during an RPC.""" - call = event.tag - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - sequence_number = rpc_state.sequence_number - rpc_state.sequence_number += 1 - if event.bytes is None: - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, - None, None) - else: - call.read(call) - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, - None, rpc_state.deserializer(event.bytes), None) - - self._rear_link.accept_front_to_back_ticket(ticket) - - def _on_write_event(self, event): - call = event.tag - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - if rpc_state.write.pending: - serialized_payload = rpc_state.write.pending.pop(0) - call.write(serialized_payload, call) - elif rpc_state.write.high is _common.HighWrite.CLOSED: - _status(call, rpc_state) - else: - rpc_state.write.low = _LowWrite.OPEN - - def _on_complete_event(self, event): - if not event.complete_accepted: - logging.error('Complete not accepted! %s', (event,)) - call = event.tag - rpc_state = self._rpc_states.pop(call, None) - if rpc_state is None: - return - - sequence_number = rpc_state.sequence_number - rpc_state.sequence_number += 1 - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, - None, None, None, None) - self._rear_link.accept_front_to_back_ticket(ticket) - - def _on_finish_event(self, event): - """Handle termination of an RPC.""" - call = event.tag - rpc_state = self._rpc_states.pop(call, None) - if rpc_state is None: - return - - code = event.status.code - if code is _low.Code.OK: - return - - sequence_number = rpc_state.sequence_number - rpc_state.sequence_number += 1 - if code is _low.Code.CANCELLED: - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.CANCELLATION, None, None, - None, None, None) - elif code is _low.Code.DEADLINE_EXCEEDED: - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.EXPIRATION, None, None, None, - None, None) - else: - # TODO(nathaniel): Better mapping of codes to ticket-categories - ticket = base_interfaces.FrontToBackTicket( - call, sequence_number, - base_interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, None, - None, None, None, None) - self._rear_link.accept_front_to_back_ticket(ticket) - - def _spin(self, completion_queue, server): - while True: - event = completion_queue.get(None) - - with self._condition: - if event.kind is _low.Event.Kind.STOP: - self._on_stop_event() - return - elif self._server is None: - continue - elif event.kind is _low.Event.Kind.SERVICE_ACCEPTED: - self._on_service_acceptance_event(event, server) - elif event.kind is _low.Event.Kind.READ_ACCEPTED: - self._on_read_event(event) - elif event.kind is _low.Event.Kind.WRITE_ACCEPTED: - self._on_write_event(event) - elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: - self._on_complete_event(event) - elif event.kind is _low.Event.Kind.FINISH: - self._on_finish_event(event) - else: - logging.error('Illegal event! %s', (event,)) - - def _continue(self, call, payload): - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - _write(call, rpc_state, payload) - - def _complete(self, call, payload): - """Handle completion of the writes of an RPC.""" - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - if rpc_state.write.low is _LowWrite.OPEN: - if payload is None: - _status(call, rpc_state) - else: - _write(call, rpc_state, payload) - elif rpc_state.write.low is _LowWrite.ACTIVE: - if payload is not None: - rpc_state.write.pending.append(rpc_state.serializer(payload)) - else: - raise ValueError('Called to complete after having already completed!') - rpc_state.write.high = _common.HighWrite.CLOSED - - def _cancel(self, call): - call.cancel() - self._rpc_states.pop(call, None) - - def join_rear_link(self, rear_link): - """See base_interfaces.ForeLink.join_rear_link for specification.""" - self._rear_link = null.NULL_REAR_LINK if rear_link is None else rear_link - - def _start(self): - """Starts this ForeLink. - - This method must be called before attempting to exchange tickets with this - object. - """ - with self._condition: - address = '[::]:%d' % ( - 0 if self._requested_port is None else self._requested_port) - self._completion_queue = _low.CompletionQueue() - if self._root_certificates is None and not self._key_chain_pairs: - self._server = _low.Server(self._completion_queue) - self._port = self._server.add_http2_addr(address) - else: - server_credentials = _low.ServerCredentials( - self._root_certificates, self._key_chain_pairs) - self._server = _low.Server(self._completion_queue) - self._port = self._server.add_secure_http2_addr( - address, server_credentials) - self._server.start() - - self._server.service(None) - - self._pool.submit(self._spin, self._completion_queue, self._server) - self._spinning = True - - return self - - # TODO(nathaniel): Expose graceful-shutdown semantics in which this object - # enters a state in which it finishes ongoing RPCs but refuses new ones. - def _stop(self): - """Stops this ForeLink. - - This method must be called for proper termination of this object, and no - attempts to exchange tickets with this object may be made after this method - has been called. - """ - with self._condition: - self._server.stop() - # TODO(nathaniel): Yep, this is weird. Deleting a server shouldn't have a - # behaviorally significant side-effect. - self._server = None - self._completion_queue.stop() - - while self._spinning: - self._condition.wait() - - self._port = None - - def __enter__(self): - """See activated.Activated.__enter__ for specification.""" - return self._start() - - def __exit__(self, exc_type, exc_val, exc_tb): - """See activated.Activated.__exit__ for specification.""" - self._stop() - return False - - def start(self): - """See activated.Activated.start for specification.""" - return self._start() - - def stop(self): - """See activated.Activated.stop for specification.""" - self._stop() - - def port(self): - """Identifies the port on which this ForeLink is servicing RPCs. - - Returns: - The number of the port on which this ForeLink is servicing RPCs, or None - if this ForeLink is not currently activated and servicing RPCs. - """ - with self._condition: - return self._port - - def accept_back_to_front_ticket(self, ticket): - """See base_interfaces.ForeLink.accept_back_to_front_ticket for spec.""" - with self._condition: - if self._server is None: - return - - if ticket.kind is base_interfaces.BackToFrontTicket.Kind.CONTINUATION: - self._continue(ticket.operation_id, ticket.payload) - elif ticket.kind is base_interfaces.BackToFrontTicket.Kind.COMPLETION: - self._complete(ticket.operation_id, ticket.payload) - else: - self._cancel(ticket.operation_id) diff --git a/src/python/src/grpc/_adapter/rear.py b/src/python/src/grpc/_adapter/rear.py deleted file mode 100644 index fd6f45f7a7..0000000000 --- a/src/python/src/grpc/_adapter/rear.py +++ /dev/null @@ -1,395 +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. - -"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" - -import enum -import logging -import threading -import time - -from grpc._adapter import _common -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 -from grpc.framework.foundation import logging_pool - -_THREAD_POOL_SIZE = 10 - -_INVOCATION_EVENT_KINDS = ( - _low.Event.Kind.METADATA_ACCEPTED, - _low.Event.Kind.FINISH -) - - -@enum.unique -class _LowWrite(enum.Enum): - """The possible categories of low-level write state.""" - - OPEN = 'OPEN' - ACTIVE = 'ACTIVE' - CLOSED = 'CLOSED' - - -class _RPCState(object): - """The full state of any tracked RPC. - - Attributes: - call: The _low.Call object for the RPC. - outstanding: The set of Event.Kind values describing expected future events - for the RPC. - active: A boolean indicating whether or not the RPC is active. - common: An _common.RPCState describing additional state for the RPC. - """ - - def __init__(self, call, outstanding, active, common): - self.call = call - self.outstanding = outstanding - self.active = active - self.common = common - - -def _write(operation_id, call, outstanding, write_state, serialized_payload): - if write_state.low is _LowWrite.OPEN: - call.write(serialized_payload, operation_id) - outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) - write_state.low = _LowWrite.ACTIVE - elif write_state.low is _LowWrite.ACTIVE: - write_state.pending.append(serialized_payload) - else: - raise ValueError('Write attempted after writes completed!') - - -class RearLink(base_interfaces.RearLink, activated.Activated): - """An invocation-side bridge between RPC Framework and the C-ish _low code.""" - - def __init__( - self, host, port, pool, request_serializers, response_deserializers, - secure, root_certificates, private_key, certificate_chain, - metadata_transformer=None, server_host_override=None): - """Constructor. - - Args: - host: The host to which to connect for RPC service. - port: The port to which to connect for RPC service. - pool: A thread pool. - request_serializers: A dict from RPC method names to request object - serializer behaviors. - response_deserializers: A dict from RPC method names to response object - deserializer behaviors. - secure: A boolean indicating whether or not to use a secure connection. - root_certificates: The PEM-encoded root certificates or None to ask for - them to be retrieved from a default location. - private_key: The PEM-encoded private key to use or None if no private - key should be used. - certificate_chain: The PEM-encoded certificate chain to use or None if - no certificate chain should be used. - metadata_transformer: A function that given a metadata object produces - another metadata to be used in the underlying communication on the - wire. - server_host_override: (For testing only) the target name used for SSL - host name checking. - """ - self._condition = threading.Condition() - self._host = host - self._port = port - self._pool = pool - self._request_serializers = request_serializers - self._response_deserializers = response_deserializers - - self._fore_link = null.NULL_FORE_LINK - self._completion_queue = None - self._channel = None - self._rpc_states = {} - self._spinning = False - if secure: - self._client_credentials = _low.ClientCredentials( - root_certificates, private_key, certificate_chain) - else: - self._client_credentials = None - self._root_certificates = root_certificates - self._private_key = private_key - self._certificate_chain = certificate_chain - self._metadata_transformer = metadata_transformer - self._server_host_override = server_host_override - - def _on_write_event(self, operation_id, event, rpc_state): - if event.write_accepted: - if rpc_state.common.write.pending: - rpc_state.call.write( - rpc_state.common.write.pending.pop(0), operation_id) - rpc_state.outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) - elif rpc_state.common.write.high is _common.HighWrite.CLOSED: - rpc_state.call.complete(operation_id) - rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) - rpc_state.common.write.low = _LowWrite.CLOSED - else: - rpc_state.common.write.low = _LowWrite.OPEN - else: - logging.error('RPC write not accepted! Event: %s', (event,)) - rpc_state.active = False - ticket = base_interfaces.BackToFrontTicket( - operation_id, rpc_state.common.sequence_number, - base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) - rpc_state.common.sequence_number += 1 - self._fore_link.accept_back_to_front_ticket(ticket) - - def _on_read_event(self, operation_id, event, rpc_state): - if event.bytes is not None: - rpc_state.call.read(operation_id) - rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) - - ticket = base_interfaces.BackToFrontTicket( - operation_id, rpc_state.common.sequence_number, - base_interfaces.BackToFrontTicket.Kind.CONTINUATION, - rpc_state.common.deserializer(event.bytes)) - rpc_state.common.sequence_number += 1 - self._fore_link.accept_back_to_front_ticket(ticket) - - def _on_complete_event(self, operation_id, event, rpc_state): - if not event.complete_accepted: - logging.error('RPC complete not accepted! Event: %s', (event,)) - rpc_state.active = False - ticket = base_interfaces.BackToFrontTicket( - operation_id, rpc_state.common.sequence_number, - base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, None) - rpc_state.common.sequence_number += 1 - self._fore_link.accept_back_to_front_ticket(ticket) - - # TODO(nathaniel): Metadata support. - def _on_metadata_event(self, operation_id, event, rpc_state): # pylint: disable=unused-argument - rpc_state.call.read(operation_id) - rpc_state.outstanding.add(_low.Event.Kind.READ_ACCEPTED) - - def _on_finish_event(self, operation_id, event, rpc_state): - """Handle termination of an RPC.""" - # TODO(nathaniel): Cover all statuses. - if event.status.code is _low.Code.OK: - 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.DEADLINE_EXCEEDED: - kind = base_interfaces.BackToFrontTicket.Kind.EXPIRATION - else: - kind = base_interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE - ticket = base_interfaces.BackToFrontTicket( - operation_id, rpc_state.common.sequence_number, kind, None) - rpc_state.common.sequence_number += 1 - self._fore_link.accept_back_to_front_ticket(ticket) - - def _spin(self, completion_queue): - while True: - event = completion_queue.get(None) - operation_id = event.tag - - with self._condition: - rpc_state = self._rpc_states[operation_id] - rpc_state.outstanding.remove(event.kind) - if rpc_state.active and self._completion_queue is not None: - if event.kind is _low.Event.Kind.WRITE_ACCEPTED: - self._on_write_event(operation_id, event, rpc_state) - elif event.kind is _low.Event.Kind.METADATA_ACCEPTED: - self._on_metadata_event(operation_id, event, rpc_state) - elif event.kind is _low.Event.Kind.READ_ACCEPTED: - self._on_read_event(operation_id, event, rpc_state) - elif event.kind is _low.Event.Kind.COMPLETE_ACCEPTED: - self._on_complete_event(operation_id, event, rpc_state) - elif event.kind is _low.Event.Kind.FINISH: - self._on_finish_event(operation_id, event, rpc_state) - else: - logging.error('Illegal RPC event! %s', (event,)) - - if not rpc_state.outstanding: - self._rpc_states.pop(operation_id) - if not self._rpc_states: - self._spinning = False - self._condition.notify_all() - return - - def _invoke(self, operation_id, name, high_state, payload, timeout): - """Invoke an RPC. - - Args: - operation_id: Any object to be used as an operation ID for the RPC. - name: The RPC method name. - high_state: A _common.HighWrite value representing the "high write state" - of the RPC. - payload: A payload object for the RPC or None if no payload was given at - invocation-time. - timeout: A duration of time in seconds to allow for the RPC. - """ - request_serializer = self._request_serializers[name] - call = _low.Call(self._channel, self._completion_queue, name, self._host, time.time() + timeout) - if self._metadata_transformer is not None: - metadata = self._metadata_transformer([]) - for metadata_key, metadata_value in metadata: - call.add_metadata(metadata_key, metadata_value) - call.invoke(self._completion_queue, operation_id, operation_id) - outstanding = set(_INVOCATION_EVENT_KINDS) - - if payload is None: - if high_state is _common.HighWrite.CLOSED: - call.complete(operation_id) - low_state = _LowWrite.CLOSED - outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) - else: - low_state = _LowWrite.OPEN - else: - serialized_payload = request_serializer(payload) - call.write(serialized_payload, operation_id) - outstanding.add(_low.Event.Kind.WRITE_ACCEPTED) - low_state = _LowWrite.ACTIVE - - write_state = _common.WriteState(low_state, high_state, []) - common_state = _common.CommonRPCState( - write_state, 0, self._response_deserializers[name], request_serializer) - self._rpc_states[operation_id] = _RPCState( - call, outstanding, True, common_state) - - if not self._spinning: - self._pool.submit(self._spin, self._completion_queue) - self._spinning = True - - def _commence(self, operation_id, name, payload, timeout): - self._invoke(operation_id, name, _common.HighWrite.OPEN, payload, timeout) - - def _continue(self, operation_id, payload): - rpc_state = self._rpc_states.get(operation_id, None) - if rpc_state is None or not rpc_state.active: - return - - _write( - operation_id, rpc_state.call, rpc_state.outstanding, - rpc_state.common.write, rpc_state.common.serializer(payload)) - - def _complete(self, operation_id, payload): - """Close writes associated with an ongoing RPC. - - Args: - operation_id: Any object being use as an operation ID for the RPC. - payload: A payload object for the RPC (and thus the last payload object - for the RPC) or None if no payload was given along with the instruction - to indicate the end of writes for the RPC. - """ - rpc_state = self._rpc_states.get(operation_id, None) - if rpc_state is None or not rpc_state.active: - return - - write_state = rpc_state.common.write - if payload is None: - if write_state.low is _LowWrite.OPEN: - rpc_state.call.complete(operation_id) - rpc_state.outstanding.add(_low.Event.Kind.COMPLETE_ACCEPTED) - write_state.low = _LowWrite.CLOSED - else: - _write( - operation_id, rpc_state.call, rpc_state.outstanding, write_state, - rpc_state.common.serializer(payload)) - write_state.high = _common.HighWrite.CLOSED - - def _entire(self, operation_id, name, payload, timeout): - self._invoke(operation_id, name, _common.HighWrite.CLOSED, payload, timeout) - - def _cancel(self, operation_id): - rpc_state = self._rpc_states.get(operation_id, None) - if rpc_state is not None and rpc_state.active: - rpc_state.call.cancel() - rpc_state.active = False - - def join_fore_link(self, fore_link): - """See base_interfaces.RearLink.join_fore_link for specification.""" - with self._condition: - self._fore_link = null.NULL_FORE_LINK if fore_link is None else fore_link - - def _start(self): - """Starts this RearLink. - - This method must be called before attempting to exchange tickets with this - object. - """ - with self._condition: - self._completion_queue = _low.CompletionQueue() - self._channel = _low.Channel( - '%s:%d' % (self._host, self._port), self._client_credentials, - server_host_override=self._server_host_override) - return self - - def _stop(self): - """Stops this RearLink. - - This method must be called for proper termination of this object, and no - attempts to exchange tickets with this object may be made after this method - has been called. - """ - with self._condition: - self._completion_queue.stop() - self._completion_queue = None - - while self._spinning: - self._condition.wait() - - def __enter__(self): - """See activated.Activated.__enter__ for specification.""" - return self._start() - - def __exit__(self, exc_type, exc_val, exc_tb): - """See activated.Activated.__exit__ for specification.""" - self._stop() - return False - - def start(self): - """See activated.Activated.start for specification.""" - return self._start() - - def stop(self): - """See activated.Activated.stop for specification.""" - self._stop() - - def accept_front_to_back_ticket(self, ticket): - """See base_interfaces.RearLink.accept_front_to_back_ticket for spec.""" - with self._condition: - if self._completion_queue is None: - return - - if ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMMENCEMENT: - self._commence( - ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) - elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CONTINUATION: - self._continue(ticket.operation_id, ticket.payload) - elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.COMPLETION: - self._complete(ticket.operation_id, ticket.payload) - elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.ENTIRE: - self._entire( - ticket.operation_id, ticket.name, ticket.payload, ticket.timeout) - elif ticket.kind is base_interfaces.FrontToBackTicket.Kind.CANCELLATION: - self._cancel(ticket.operation_id) - else: - # NOTE(nathaniel): All other categories are treated as cancellation. - self._cancel(ticket.operation_id) diff --git a/src/python/src/grpc/_cython/.gitignore b/src/python/src/grpc/_cython/.gitignore deleted file mode 100644 index c315029288..0000000000 --- a/src/python/src/grpc/_cython/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -*.h -*.c -*.a -*.so -*.dll -*.pyc -*.pyd diff --git a/src/python/src/grpc/_cython/README.rst b/src/python/src/grpc/_cython/README.rst deleted file mode 100644 index c0e66734e8..0000000000 --- a/src/python/src/grpc/_cython/README.rst +++ /dev/null @@ -1,52 +0,0 @@ -GRPC Python Cython layer -======================== - -Package for the GRPC Python Cython layer. - -What is Cython? ---------------- - -Cython is both a superset of the Python language with extensions for dealing -with C types and a tool that transpiles this superset into C code. It provides -convenient means of statically typing expressions and of converting Python -strings to pointers (among other niceties), thus dramatically smoothing the -Python/C interop by allowing fluid use of APIs in both from the same source. -See the wonderful `Cython website`_. - -Why Cython? ------------ - -- **Python 2 and 3 support** - Cython generated C code has precompiler macros to target both Python 2 and - Python 3 C APIs, even while acting as a superset of just the Python 2 - language (e.g. using ``basestring``). -- **Significantly less semantic noise** - A lot of CPython code is just glue, especially human-error-prone - ``Py_INCREF``-ing and ``Py_DECREF``-ing around error handlers and such. - Cython takes care of that automagically. -- **Possible PyPy support** - One of the major developments in Cython over the past few years was the - addition of support for PyPy. We might soon be able to provide such support - ourselves through our use of Cython. -- **Less Python glue code** - There existed several adapter layers in and around the original CPython code - to smooth the surface exposed to Python due to how much trouble it was to - make such a smooth surface via the CPython API alone. Cython makes writing - such a surface incredibly easy, so these adapter layers may be removed. - -Implications for Users ----------------------- - -Nothing additional will be required for users. PyPI packages will contain -Cython generated C code and thus not necessitate a Cython installation. - -Implications for GRPC Developers --------------------------------- - -A typical edit-compile-debug cycle now requires Cython. We install Cython in -the ``virtualenv`` generated for the Python tests in this repository, so -initial test runs may take an extra 2+ minutes to complete. Subsequent test -runs won't reinstall ``Cython`` (unless required versions change and the -``virtualenv`` doesn't have installed versions that satisfy the change). - -.. _`Cython website`: http://cython.org/ diff --git a/src/python/src/grpc/_cython/__init__.py b/src/python/src/grpc/_cython/__init__.py deleted file mode 100644 index b89398809f..0000000000 --- a/src/python/src/grpc/_cython/__init__.py +++ /dev/null @@ -1,28 +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. diff --git a/src/python/src/grpc/_cython/_cygrpc/__init__.py b/src/python/src/grpc/_cython/_cygrpc/__init__.py deleted file mode 100644 index b89398809f..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/__init__.py +++ /dev/null @@ -1,28 +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. diff --git a/src/python/src/grpc/_cython/_cygrpc/call.pxd b/src/python/src/grpc/_cython/_cygrpc/call.pxd deleted file mode 100644 index fe9b81e3d3..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/call.pxd +++ /dev/null @@ -1,37 +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. - -from grpc._cython._cygrpc cimport grpc - - -cdef class Call: - - cdef grpc.grpc_call *c_call - cdef list references - diff --git a/src/python/src/grpc/_cython/_cygrpc/call.pyx b/src/python/src/grpc/_cython/_cygrpc/call.pyx deleted file mode 100644 index 4349786b3a..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/call.pyx +++ /dev/null @@ -1,82 +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. - -cimport cpython - -from grpc._cython._cygrpc cimport records - - -cdef class Call: - - def __cinit__(self): - # Create an *empty* call - self.c_call = NULL - self.references = [] - - def start_batch(self, operations, tag): - if not self.is_valid: - raise ValueError("invalid call object cannot be used from Python") - cdef records.Operations cy_operations = records.Operations(operations) - cdef records.OperationTag operation_tag = records.OperationTag(tag) - operation_tag.operation_call = self - operation_tag.batch_operations = cy_operations - cpython.Py_INCREF(operation_tag) - return grpc.grpc_call_start_batch( - self.c_call, cy_operations.c_ops, cy_operations.c_nops, - operation_tag) - - def cancel(self, - grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, - details=None): - if not self.is_valid: - raise ValueError("invalid call object cannot be used from Python") - if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): - raise ValueError("if error_code is specified, so must details " - "(and vice-versa)") - if isinstance(details, bytes): - pass - elif isinstance(details, basestring): - details = details.encode() - else: - raise TypeError("expected details to be str or bytes") - if error_code != grpc.GRPC_STATUS__DO_NOT_USE: - self.references.append(details) - return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details) - else: - return grpc.grpc_call_cancel(self.c_call) - - def __dealloc__(self): - if self.c_call != NULL: - grpc.grpc_call_destroy(self.c_call) - - # The object *should* always be valid from Python. Used for debugging. - @property - def is_valid(self): - return self.c_call != NULL - diff --git a/src/python/src/grpc/_cython/_cygrpc/channel.pxd b/src/python/src/grpc/_cython/_cygrpc/channel.pxd deleted file mode 100644 index 3e341bf222..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/channel.pxd +++ /dev/null @@ -1,36 +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. - -from grpc._cython._cygrpc cimport grpc - - -cdef class Channel: - - cdef grpc.grpc_channel *c_channel - cdef list references diff --git a/src/python/src/grpc/_cython/_cygrpc/channel.pyx b/src/python/src/grpc/_cython/_cygrpc/channel.pyx deleted file mode 100644 index b20313818d..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/channel.pyx +++ /dev/null @@ -1,84 +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. - -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport completion_queue -from grpc._cython._cygrpc cimport credentials -from grpc._cython._cygrpc cimport records - - -cdef class Channel: - - def __cinit__(self, target, records.ChannelArgs arguments=None, - credentials.ClientCredentials client_credentials=None): - cdef grpc.grpc_channel_args *c_arguments = NULL - self.c_channel = NULL - self.references = [] - if arguments is not None: - c_arguments = &arguments.c_args - if isinstance(target, bytes): - pass - elif isinstance(target, basestring): - target = target.encode() - else: - raise TypeError("expected target to be str or bytes") - if client_credentials is None: - self.c_channel = grpc.grpc_channel_create(target, c_arguments) - else: - self.c_channel = grpc.grpc_secure_channel_create( - client_credentials.c_credentials, target, c_arguments) - self.references.append(client_credentials) - self.references.append(target) - self.references.append(arguments) - - def create_call(self, completion_queue.CompletionQueue queue not None, - method, host, records.Timespec deadline not None): - if queue.is_shutting_down: - raise ValueError("queue must not be shutting down or shutdown") - if isinstance(method, bytes): - pass - elif isinstance(method, basestring): - method = method.encode() - else: - raise TypeError("expected method to be str or bytes") - if isinstance(host, bytes): - pass - elif isinstance(host, basestring): - host = host.encode() - else: - raise TypeError("expected host to be str or bytes") - cdef call.Call operation_call = call.Call() - operation_call.references = [self, method, host, queue] - operation_call.c_call = grpc.grpc_channel_create_call( - self.c_channel, queue.c_completion_queue, method, host, deadline.c_time) - return operation_call - - def __dealloc__(self): - if self.c_channel != NULL: - grpc.grpc_channel_destroy(self.c_channel) diff --git a/src/python/src/grpc/_cython/_cygrpc/completion_queue.pxd b/src/python/src/grpc/_cython/_cygrpc/completion_queue.pxd deleted file mode 100644 index fd562ad75b..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/completion_queue.pxd +++ /dev/null @@ -1,39 +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. - -from grpc._cython._cygrpc cimport grpc - - -cdef class CompletionQueue: - - cdef grpc.grpc_completion_queue *c_completion_queue - cdef object poll_condition - cdef bint is_polling - cdef bint is_shutting_down - cdef bint is_shutdown diff --git a/src/python/src/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/src/grpc/_cython/_cygrpc/completion_queue.pyx deleted file mode 100644 index 886d85360a..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/completion_queue.pyx +++ /dev/null @@ -1,117 +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. - -cimport cpython - -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport records - -import threading -import time - - -cdef class CompletionQueue: - - def __cinit__(self): - self.c_completion_queue = grpc.grpc_completion_queue_create() - self.is_shutting_down = False - self.is_shutdown = False - self.poll_condition = threading.Condition() - self.is_polling = False - - def poll(self, records.Timespec deadline=None): - # We name this 'poll' to avoid problems with CPython's expectations for - # 'special' methods (like next and __next__). - cdef grpc.gpr_timespec c_deadline = grpc.gpr_inf_future - cdef records.OperationTag tag = None - cdef object user_tag = None - cdef call.Call operation_call = None - cdef records.CallDetails request_call_details = None - cdef records.Metadata request_metadata = None - cdef records.Operations batch_operations = None - if deadline is not None: - c_deadline = deadline.c_time - cdef grpc.grpc_event event - - # Poll within a critical section - with self.poll_condition: - while self.is_polling: - self.poll_condition.wait(float(deadline) - time.time()) - self.is_polling = True - with nogil: - event = grpc.grpc_completion_queue_next( - self.c_completion_queue, c_deadline) - with self.poll_condition: - self.is_polling = False - self.poll_condition.notify() - - if event.type == grpc.GRPC_QUEUE_TIMEOUT: - return records.Event(event.type, False, None, None, None, None, None) - elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: - self.is_shutdown = True - return records.Event(event.type, True, None, None, None, None, None) - else: - if event.tag != NULL: - tag = event.tag - # We receive event tags only after they've been inc-ref'd elsewhere in - # the code. - cpython.Py_DECREF(tag) - if tag.shutting_down_server is not None: - tag.shutting_down_server.notify_shutdown_complete() - user_tag = tag.user_tag - operation_call = tag.operation_call - request_call_details = tag.request_call_details - request_metadata = tag.request_metadata - batch_operations = tag.batch_operations - if tag.is_new_request: - # Stuff in the tag not explicitly handled by us needs to live through - # the life of the call - operation_call.references.extend(tag.references) - return records.Event( - event.type, event.success, user_tag, operation_call, - request_call_details, request_metadata, batch_operations) - - def shutdown(self): - grpc.grpc_completion_queue_shutdown(self.c_completion_queue) - self.is_shutting_down = True - - def clear(self): - if not self.is_shutting_down: - raise ValueError('queue must be shutting down to be cleared') - while self.poll().type != grpc.GRPC_QUEUE_SHUTDOWN: - pass - - def __dealloc__(self): - if self.c_completion_queue != NULL: - # Ensure shutdown, pump the queue - if not self.is_shutting_down: - self.shutdown() - while not self.is_shutdown: - self.poll() - grpc.grpc_completion_queue_destroy(self.c_completion_queue) diff --git a/src/python/src/grpc/_cython/_cygrpc/credentials.pxd b/src/python/src/grpc/_cython/_cygrpc/credentials.pxd deleted file mode 100644 index 6b74a267e0..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/credentials.pxd +++ /dev/null @@ -1,45 +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. - -from grpc._cython._cygrpc cimport grpc - - -cdef class ClientCredentials: - - cdef grpc.grpc_credentials *c_credentials - cdef grpc.grpc_ssl_pem_key_cert_pair c_ssl_pem_key_cert_pair - cdef list references - - -cdef class ServerCredentials: - - cdef grpc.grpc_server_credentials *c_credentials - cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs - cdef size_t c_ssl_pem_key_cert_pairs_count - cdef list references diff --git a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx b/src/python/src/grpc/_cython/_cygrpc/credentials.pyx deleted file mode 100644 index 2d74702fbd..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/credentials.pyx +++ /dev/null @@ -1,207 +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. - -from grpc._cython._cygrpc cimport records - - -cdef class ClientCredentials: - - def __cinit__(self): - self.c_credentials = NULL - self.c_ssl_pem_key_cert_pair.private_key = NULL - self.c_ssl_pem_key_cert_pair.certificate_chain = NULL - self.references = [] - - # The object *can* be invalid in Python if we fail to make the credentials - # (and the core thus returns NULL credentials). Used primarily for debugging. - @property - def is_valid(self): - return self.c_credentials != NULL - - def __dealloc__(self): - if self.c_credentials != NULL: - grpc.grpc_credentials_release(self.c_credentials) - - -cdef class ServerCredentials: - - def __cinit__(self): - self.c_credentials = NULL - - def __dealloc__(self): - if self.c_credentials != NULL: - grpc.grpc_server_credentials_release(self.c_credentials) - - -def client_credentials_google_default(): - cdef ClientCredentials credentials = ClientCredentials(); - credentials.c_credentials = grpc.grpc_google_default_credentials_create() - return credentials - -def client_credentials_ssl(pem_root_certificates, - records.SslPemKeyCertPair ssl_pem_key_cert_pair): - if pem_root_certificates is None: - pass - elif isinstance(pem_root_certificates, bytes): - pass - elif isinstance(pem_root_certificates, basestring): - pem_root_certificates = pem_root_certificates.encode() - else: - raise TypeError("expected str or bytes for pem_root_certificates") - cdef ClientCredentials credentials = ClientCredentials() - cdef const char *c_pem_root_certificates = NULL - if pem_root_certificates is not None: - c_pem_root_certificates = pem_root_certificates - credentials.references.append(pem_root_certificates) - if ssl_pem_key_cert_pair is not None: - credentials.c_credentials = grpc.grpc_ssl_credentials_create( - c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair - ) - credentials.references.append(ssl_pem_key_cert_pair) - else: - credentials.c_credentials = grpc.grpc_ssl_credentials_create( - c_pem_root_certificates, NULL - ) - -def client_credentials_composite_credentials( - ClientCredentials credentials_1 not None, - ClientCredentials credentials_2 not None): - if not credentials_1.is_valid or not credentials_2.is_valid: - raise ValueError("passed credentials must both be valid") - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_composite_credentials_create( - credentials_1.c_credentials, credentials_2.c_credentials) - credentials.references.append(credentials_1) - credentials.references.append(credentials_2) - return credentials - -def client_credentials_compute_engine(): - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_compute_engine_credentials_create() - return credentials - -def client_credentials_service_account( - json_key, scope, records.Timespec token_lifetime not None): - if isinstance(json_key, bytes): - pass - elif isinstance(json_key, basestring): - json_key = json_key.encode() - else: - raise TypeError("expected json_key to be str or bytes") - if isinstance(scope, bytes): - pass - elif isinstance(scope, basestring): - scope = scope.encode() - else: - raise TypeError("expected scope to be str or bytes") - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_service_account_credentials_create( - json_key, scope, token_lifetime.c_time) - credentials.references.extend([json_key, scope]) - return credentials - -#TODO rename to something like client_credentials_service_account_jwt_access. -def client_credentials_jwt(json_key, records.Timespec token_lifetime not None): - if isinstance(json_key, bytes): - pass - elif isinstance(json_key, basestring): - json_key = json_key.encode() - else: - raise TypeError("expected json_key to be str or bytes") - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_service_account_jwt_access_credentials_create( - json_key, token_lifetime.c_time) - credentials.references.append(json_key) - return credentials - -def client_credentials_refresh_token(json_refresh_token): - if isinstance(json_refresh_token, bytes): - pass - elif isinstance(json_refresh_token, basestring): - json_refresh_token = json_refresh_token.encode() - else: - raise TypeError("expected json_refresh_token to be str or bytes") - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_refresh_token_credentials_create( - json_refresh_token) - credentials.references.append(json_refresh_token) - return credentials - -def client_credentials_iam(authorization_token, authority_selector): - if isinstance(authorization_token, bytes): - pass - elif isinstance(authorization_token, basestring): - authorization_token = authorization_token.encode() - else: - raise TypeError("expected authorization_token to be str or bytes") - if isinstance(authority_selector, bytes): - pass - elif isinstance(authority_selector, basestring): - authority_selector = authority_selector.encode() - else: - raise TypeError("expected authority_selector to be str or bytes") - cdef ClientCredentials credentials = ClientCredentials() - credentials.c_credentials = grpc.grpc_iam_credentials_create( - authorization_token, authority_selector) - credentials.references.append(authorization_token) - credentials.references.append(authority_selector) - return credentials - -def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs): - if pem_root_certs is None: - pass - elif isinstance(pem_root_certs, bytes): - pass - elif isinstance(pem_root_certs, basestring): - pem_root_certs = pem_root_certs.encode() - else: - raise TypeError("expected pem_root_certs to be str or bytes") - pem_key_cert_pairs = list(pem_key_cert_pairs) - for pair in pem_key_cert_pairs: - if not isinstance(pair, records.SslPemKeyCertPair): - raise TypeError("expected pem_key_cert_pairs to be sequence of " - "records.SslPemKeyCertPair") - cdef ServerCredentials credentials = ServerCredentials() - credentials.references.append(pem_key_cert_pairs) - credentials.references.append(pem_root_certs) - credentials.c_ssl_pem_key_cert_pairs_count = len(pem_key_cert_pairs) - credentials.c_ssl_pem_key_cert_pairs = ( - grpc.gpr_malloc( - sizeof(grpc.grpc_ssl_pem_key_cert_pair) * - credentials.c_ssl_pem_key_cert_pairs_count - )) - for i in range(credentials.c_ssl_pem_key_cert_pairs_count): - credentials.c_ssl_pem_key_cert_pairs[i] = ( - (pem_key_cert_pairs[i]).c_pair) - credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( - pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, - credentials.c_ssl_pem_key_cert_pairs_count - ) - return credentials - diff --git a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd b/src/python/src/grpc/_cython/_cygrpc/grpc.pxd deleted file mode 100644 index d065383587..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/grpc.pxd +++ /dev/null @@ -1,342 +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. - -cimport libc.time - - -cdef extern from "grpc/support/alloc.h": - void *gpr_malloc(size_t size) - void gpr_free(void *ptr) - void *gpr_realloc(void *p, size_t size) - -cdef extern from "grpc/support/slice.h": - ctypedef struct gpr_slice: - # don't worry about writing out the members of gpr_slice; we never access - # them directly. - pass - - gpr_slice gpr_slice_ref(gpr_slice s) - void gpr_slice_unref(gpr_slice s) - gpr_slice gpr_slice_new(void *p, size_t len, void (*destroy)(void *)) - gpr_slice gpr_slice_new_with_len( - void *p, size_t len, void (*destroy)(void *, size_t)) - gpr_slice gpr_slice_malloc(size_t length) - gpr_slice gpr_slice_from_copied_string(const char *source) - gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len) - - # Declare functions for function-like macros (because Cython)... - void *gpr_slice_start_ptr "GPR_SLICE_START_PTR" (gpr_slice s) - size_t gpr_slice_length "GPR_SLICE_LENGTH" (gpr_slice s) - - -cdef extern from "grpc/support/port_platform.h": - # As long as the header file gets this type right, we don't need to get this - # type exactly; just close enough that the operations will be supported in the - # underlying C layers. - ctypedef unsigned int gpr_uint32 - - -cdef extern from "grpc/support/time.h": - - ctypedef struct gpr_timespec: - libc.time.time_t seconds "tv_sec" - int nanoseconds "tv_nsec" - - cdef gpr_timespec gpr_time_0 - cdef gpr_timespec gpr_inf_future - cdef gpr_timespec gpr_inf_past - - gpr_timespec gpr_now() - - -cdef extern from "grpc/status.h": - ctypedef enum grpc_status_code: - GRPC_STATUS_OK - GRPC_STATUS_CANCELLED - GRPC_STATUS_UNKNOWN - GRPC_STATUS_INVALID_ARGUMENT - GRPC_STATUS_DEADLINE_EXCEEDED - GRPC_STATUS_NOT_FOUND - GRPC_STATUS_ALREADY_EXISTS - GRPC_STATUS_PERMISSION_DENIED - GRPC_STATUS_UNAUTHENTICATED - GRPC_STATUS_RESOURCE_EXHAUSTED - GRPC_STATUS_FAILED_PRECONDITION - GRPC_STATUS_ABORTED - GRPC_STATUS_OUT_OF_RANGE - GRPC_STATUS_UNIMPLEMENTED - GRPC_STATUS_INTERNAL - GRPC_STATUS_UNAVAILABLE - GRPC_STATUS_DATA_LOSS - GRPC_STATUS__DO_NOT_USE - - -cdef extern from "grpc/byte_buffer_reader.h": - struct grpc_byte_buffer_reader: - # We don't care about the internals - pass - - -cdef extern from "grpc/byte_buffer.h": - ctypedef struct grpc_byte_buffer: - # We don't care about the internals. - pass - - grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, - size_t nslices) - size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) - void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer) - - void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, - grpc_byte_buffer *buffer) - int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, - gpr_slice *slice) - void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) - - -cdef extern from "grpc/grpc.h": - - ctypedef struct grpc_completion_queue: - # We don't care about the internals (and in fact don't know them) - pass - - ctypedef struct grpc_channel: - # We don't care about the internals (and in fact don't know them) - pass - - ctypedef struct grpc_server: - # We don't care about the internals (and in fact don't know them) - pass - - ctypedef struct grpc_call: - # We don't care about the internals (and in fact don't know them) - pass - - ctypedef enum grpc_arg_type: - grpc_arg_string "GRPC_ARG_STRING" - grpc_arg_integer "GRPC_ARG_INTEGER" - grpc_arg_pointer "GRPC_ARG_POINTER" - - ctypedef struct grpc_arg_value_pointer: - void *address "p" - void *(*copy)(void *) - void (*destroy)(void *) - - union grpc_arg_value: - char *string - int integer - grpc_arg_value_pointer pointer - - ctypedef struct grpc_arg: - grpc_arg_type type - char *key - grpc_arg_value value - - ctypedef struct grpc_channel_args: - size_t arguments_length "num_args" - grpc_arg *arguments "args" - - ctypedef enum grpc_call_error: - GRPC_CALL_OK - GRPC_CALL_ERROR - GRPC_CALL_ERROR_NOT_ON_SERVER - GRPC_CALL_ERROR_NOT_ON_CLIENT - GRPC_CALL_ERROR_ALREADY_ACCEPTED - GRPC_CALL_ERROR_ALREADY_INVOKED - GRPC_CALL_ERROR_NOT_INVOKED - GRPC_CALL_ERROR_ALREADY_FINISHED - GRPC_CALL_ERROR_TOO_MANY_OPERATIONS - GRPC_CALL_ERROR_INVALID_FLAGS - GRPC_CALL_ERROR_INVALID_METADATA - - ctypedef struct grpc_metadata: - const char *key - const char *value - size_t value_length - # ignore the 'internal_data.obfuscated' fields. - - ctypedef enum grpc_completion_type: - GRPC_QUEUE_SHUTDOWN - GRPC_QUEUE_TIMEOUT - GRPC_OP_COMPLETE - - ctypedef struct grpc_event: - grpc_completion_type type - int success - void *tag - - ctypedef struct grpc_metadata_array: - size_t count - size_t capacity - grpc_metadata *metadata - - void grpc_metadata_array_init(grpc_metadata_array *array) - void grpc_metadata_array_destroy(grpc_metadata_array *array) - - ctypedef struct grpc_call_details: - char *method - size_t method_capacity - char *host - size_t host_capacity - gpr_timespec deadline - - void grpc_call_details_init(grpc_call_details *details) - void grpc_call_details_destroy(grpc_call_details *details) - - ctypedef enum grpc_op_type: - GRPC_OP_SEND_INITIAL_METADATA - GRPC_OP_SEND_MESSAGE - GRPC_OP_SEND_CLOSE_FROM_CLIENT - GRPC_OP_SEND_STATUS_FROM_SERVER - GRPC_OP_RECV_INITIAL_METADATA - GRPC_OP_RECV_MESSAGE - GRPC_OP_RECV_STATUS_ON_CLIENT - GRPC_OP_RECV_CLOSE_ON_SERVER - - ctypedef struct grpc_op_data_send_initial_metadata: - size_t count - grpc_metadata *metadata - - ctypedef struct grpc_op_data_send_status_from_server: - size_t trailing_metadata_count - grpc_metadata *trailing_metadata - grpc_status_code status - const char *status_details - - ctypedef struct grpc_op_data_recv_status_on_client: - grpc_metadata_array *trailing_metadata - grpc_status_code *status - char **status_details - size_t *status_details_capacity - - ctypedef struct grpc_op_data_recv_close_on_server: - int *cancelled - - union grpc_op_data: - grpc_op_data_send_initial_metadata send_initial_metadata - grpc_byte_buffer *send_message - grpc_op_data_send_status_from_server send_status_from_server - grpc_metadata_array *receive_initial_metadata "recv_initial_metadata" - grpc_byte_buffer **receive_message "recv_message" - grpc_op_data_recv_status_on_client receive_status_on_client "recv_status_on_client" - grpc_op_data_recv_close_on_server receive_close_on_server "recv_close_on_server" - - ctypedef struct grpc_op: - grpc_op_type type "op" - gpr_uint32 flags - grpc_op_data data - - void grpc_init() - void grpc_shutdown() - - grpc_completion_queue *grpc_completion_queue_create() - grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, - gpr_timespec deadline) nogil - void grpc_completion_queue_shutdown(grpc_completion_queue *cq) - void grpc_completion_queue_destroy(grpc_completion_queue *cq) - - grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, - size_t nops, void *tag) - grpc_call_error grpc_call_cancel(grpc_call *call) - grpc_call_error grpc_call_cancel_with_status(grpc_call *call, - grpc_status_code status, - const char *description) - void grpc_call_destroy(grpc_call *call) - - - grpc_channel *grpc_channel_create(const char *target, - const grpc_channel_args *args) - grpc_call *grpc_channel_create_call(grpc_channel *channel, - grpc_completion_queue *completion_queue, - const char *method, const char *host, - gpr_timespec deadline) - void grpc_channel_destroy(grpc_channel *channel) - - grpc_server *grpc_server_create(const grpc_channel_args *args) - grpc_call_error grpc_server_request_call( - grpc_server *server, grpc_call **call, grpc_call_details *details, - grpc_metadata_array *request_metadata, grpc_completion_queue - *cq_bound_to_call, grpc_completion_queue *cq_for_notification, void - *tag_new) - void grpc_server_register_completion_queue(grpc_server *server, - grpc_completion_queue *cq) - int grpc_server_add_http2_port(grpc_server *server, const char *addr) - void grpc_server_start(grpc_server *server) - void grpc_server_shutdown_and_notify( - grpc_server *server, grpc_completion_queue *cq, void *tag) - void grpc_server_cancel_all_calls(grpc_server *server) - void grpc_server_destroy(grpc_server *server) - - -cdef extern from "grpc/grpc_security.h": - - ctypedef struct grpc_ssl_pem_key_cert_pair: - const char *private_key - const char *certificate_chain "cert_chain" - - ctypedef struct grpc_credentials: - # We don't care about the internals (and in fact don't know them) - pass - - grpc_credentials *grpc_google_default_credentials_create() - grpc_credentials *grpc_ssl_credentials_create( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair) - - grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1, - grpc_credentials *creds2) - grpc_credentials *grpc_compute_engine_credentials_create() - grpc_credentials *grpc_service_account_credentials_create( - const char *json_key, const char *scope, gpr_timespec token_lifetime) - grpc_credentials *grpc_service_account_jwt_access_credentials_create(const char *json_key, - gpr_timespec token_lifetime) - grpc_credentials *grpc_refresh_token_credentials_create( - const char *json_refresh_token) - grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, - const char *authority_selector) - void grpc_credentials_release(grpc_credentials *creds) - - grpc_channel *grpc_secure_channel_create( - grpc_credentials *creds, const char *target, - const grpc_channel_args *args) - - ctypedef struct grpc_server_credentials: - # We don't care about the internals (and in fact don't know them) - pass - - grpc_server_credentials *grpc_ssl_server_credentials_create( - const char *pem_root_certs, - grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs); - void grpc_server_credentials_release(grpc_server_credentials *creds) - - int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, - grpc_server_credentials *creds) - - grpc_call_error grpc_call_set_credentials(grpc_call *call, - grpc_credentials *creds) diff --git a/src/python/src/grpc/_cython/_cygrpc/records.pxd b/src/python/src/grpc/_cython/_cygrpc/records.pxd deleted file mode 100644 index 9ee487882a..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/records.pxd +++ /dev/null @@ -1,129 +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. - -from grpc._cython._cygrpc cimport grpc -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport server - - -cdef class Timespec: - - cdef grpc.gpr_timespec c_time - - -cdef class CallDetails: - - cdef grpc.grpc_call_details c_details - - -cdef class OperationTag: - - cdef object user_tag - cdef list references - # This allows CompletionQueue to notify the Python Server object that the - # underlying GRPC core server has shutdown - cdef server.Server shutting_down_server - cdef call.Call operation_call - cdef CallDetails request_call_details - cdef Metadata request_metadata - cdef Operations batch_operations - cdef bint is_new_request - - -cdef class Event: - - cdef readonly grpc.grpc_completion_type type - cdef readonly bint success - cdef readonly object tag - - # For operations with calls - cdef readonly call.Call operation_call - - # For Server.request_call - cdef readonly CallDetails request_call_details - cdef readonly Metadata request_metadata - - # For Call.start_batch - cdef readonly Operations batch_operations - - -cdef class ByteBuffer: - - cdef grpc.grpc_byte_buffer *c_byte_buffer - - -cdef class SslPemKeyCertPair: - - cdef grpc.grpc_ssl_pem_key_cert_pair c_pair - cdef readonly object private_key, certificate_chain - - -cdef class ChannelArg: - - cdef grpc.grpc_arg c_arg - cdef readonly object key, value - - -cdef class ChannelArgs: - - cdef grpc.grpc_channel_args c_args - cdef list args - - -cdef class Metadatum: - - cdef grpc.grpc_metadata c_metadata - cdef object _key, _value - - -cdef class Metadata: - - cdef grpc.grpc_metadata_array c_metadata_array - cdef object metadata - - -cdef class Operation: - - cdef grpc.grpc_op c_op - cdef ByteBuffer _received_message - cdef Metadata _received_metadata - cdef grpc.grpc_status_code _received_status_code - cdef char *_received_status_details - cdef size_t _received_status_details_capacity - cdef int _received_cancelled - cdef readonly bint is_valid - cdef object references - - -cdef class Operations: - - cdef grpc.grpc_op *c_ops - cdef size_t c_nops - cdef list operations - diff --git a/src/python/src/grpc/_cython/_cygrpc/records.pyx b/src/python/src/grpc/_cython/_cygrpc/records.pyx deleted file mode 100644 index 4814769fd2..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/records.pyx +++ /dev/null @@ -1,575 +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. - -from grpc._cython._cygrpc cimport grpc -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport server - - -class StatusCode: - ok = grpc.GRPC_STATUS_OK - cancelled = grpc.GRPC_STATUS_CANCELLED - unknown = grpc.GRPC_STATUS_UNKNOWN - invalid_argument = grpc.GRPC_STATUS_INVALID_ARGUMENT - deadline_exceeded = grpc.GRPC_STATUS_DEADLINE_EXCEEDED - not_found = grpc.GRPC_STATUS_NOT_FOUND - already_exists = grpc.GRPC_STATUS_ALREADY_EXISTS - permission_denied = grpc.GRPC_STATUS_PERMISSION_DENIED - unauthenticated = grpc.GRPC_STATUS_UNAUTHENTICATED - resource_exhausted = grpc.GRPC_STATUS_RESOURCE_EXHAUSTED - failed_precondition = grpc.GRPC_STATUS_FAILED_PRECONDITION - aborted = grpc.GRPC_STATUS_ABORTED - out_of_range = grpc.GRPC_STATUS_OUT_OF_RANGE - unimplemented = grpc.GRPC_STATUS_UNIMPLEMENTED - internal = grpc.GRPC_STATUS_INTERNAL - unavailable = grpc.GRPC_STATUS_UNAVAILABLE - data_loss = grpc.GRPC_STATUS_DATA_LOSS - - -class CallError: - ok = grpc.GRPC_CALL_OK - error = grpc.GRPC_CALL_ERROR - not_on_server = grpc.GRPC_CALL_ERROR_NOT_ON_SERVER - not_on_client = grpc.GRPC_CALL_ERROR_NOT_ON_CLIENT - already_accepted = grpc.GRPC_CALL_ERROR_ALREADY_ACCEPTED - already_invoked = grpc.GRPC_CALL_ERROR_ALREADY_INVOKED - not_invoked = grpc.GRPC_CALL_ERROR_NOT_INVOKED - already_finished = grpc.GRPC_CALL_ERROR_ALREADY_FINISHED - too_many_operations = grpc.GRPC_CALL_ERROR_TOO_MANY_OPERATIONS - invalid_flags = grpc.GRPC_CALL_ERROR_INVALID_FLAGS - invalid_metadata = grpc.GRPC_CALL_ERROR_INVALID_METADATA - - -class CompletionType: - queue_shutdown = grpc.GRPC_QUEUE_SHUTDOWN - queue_timeout = grpc.GRPC_QUEUE_TIMEOUT - operation_complete = grpc.GRPC_OP_COMPLETE - - -class OperationType: - send_initial_metadata = grpc.GRPC_OP_SEND_INITIAL_METADATA - send_message = grpc.GRPC_OP_SEND_MESSAGE - send_close_from_client = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT - send_status_from_server = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER - receive_initial_metadata = grpc.GRPC_OP_RECV_INITIAL_METADATA - receive_message = grpc.GRPC_OP_RECV_MESSAGE - receive_status_on_client = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT - receive_close_on_server = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER - - -cdef class Timespec: - - def __cinit__(self, time): - if time is None: - self.c_time = grpc.gpr_now() - elif isinstance(time, float): - if time == float("+inf"): - self.c_time = grpc.gpr_inf_future - elif time == float("-inf"): - self.c_time = grpc.gpr_inf_past - else: - self.c_time.seconds = time - self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 - else: - raise TypeError("expected time to be float") - - @property - def seconds(self): - return self.c_time.seconds - - @property - def nanoseconds(self): - return self.c_time.nanoseconds - - def __float__(self): - return self.c_time.seconds + self.c_time.nanoseconds / 1e9 - - infinite_future = Timespec(float("+inf")) - infinite_past = Timespec(float("-inf")) - - -cdef class CallDetails: - - def __cinit__(self): - grpc.grpc_call_details_init(&self.c_details) - - def __dealloc__(self): - grpc.grpc_call_details_destroy(&self.c_details) - - @property - def method(self): - if self.c_details.method != NULL: - return self.c_details.method - else: - return None - - @property - def host(self): - if self.c_details.host != NULL: - return self.c_details.host - else: - return None - - @property - def deadline(self): - timespec = Timespec(float("-inf")) - timespec.c_time = self.c_details.deadline - return timespec - - -cdef class OperationTag: - - def __cinit__(self, user_tag): - self.user_tag = user_tag - self.references = [] - - -cdef class Event: - - def __cinit__(self, grpc.grpc_completion_type type, bint success, - object tag, call.Call operation_call, - CallDetails request_call_details, - Metadata request_metadata, - Operations batch_operations): - self.type = type - self.success = success - self.tag = tag - self.operation_call = operation_call - self.request_call_details = request_call_details - self.request_metadata = request_metadata - self.batch_operations = batch_operations - - -cdef class ByteBuffer: - - def __cinit__(self, data): - if data is None: - self.c_byte_buffer = NULL - return - if isinstance(data, bytes): - pass - elif isinstance(data, basestring): - data = data.encode() - else: - raise TypeError("expected value to be of type str or bytes") - - cdef char *c_data = data - data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) - self.c_byte_buffer = grpc.grpc_raw_byte_buffer_create( - &data_slice, 1) - grpc.gpr_slice_unref(data_slice) - - def bytes(self): - cdef grpc.grpc_byte_buffer_reader reader - cdef grpc.gpr_slice data_slice - cdef size_t data_slice_length - cdef void *data_slice_pointer - if self.c_byte_buffer != NULL: - grpc.grpc_byte_buffer_reader_init(&reader, self.c_byte_buffer) - result = b"" - while grpc.grpc_byte_buffer_reader_next(&reader, &data_slice): - data_slice_pointer = grpc.gpr_slice_start_ptr(data_slice) - data_slice_length = grpc.gpr_slice_length(data_slice) - result += (data_slice_pointer)[:data_slice_length] - grpc.grpc_byte_buffer_reader_destroy(&reader) - return result - else: - return None - - def __len__(self): - if self.c_byte_buffer != NULL: - return grpc.grpc_byte_buffer_length(self.c_byte_buffer) - else: - return 0 - - def __str__(self): - return self.bytes() - - def __dealloc__(self): - if self.c_byte_buffer != NULL: - grpc.grpc_byte_buffer_destroy(self.c_byte_buffer) - - -cdef class SslPemKeyCertPair: - - def __cinit__(self, private_key, certificate_chain): - if isinstance(private_key, bytes): - self.private_key = private_key - elif isinstance(private_key, basestring): - self.private_key = private_key.encode() - else: - raise TypeError("expected private_key to be of type str or bytes") - if isinstance(certificate_chain, bytes): - self.certificate_chain = certificate_chain - elif isinstance(certificate_chain, basestring): - self.certificate_chain = certificate_chain.encode() - else: - raise TypeError("expected certificate_chain to be of type str or bytes " - "or int") - self.c_pair.private_key = self.private_key - self.c_pair.certificate_chain = self.certificate_chain - - -cdef class ChannelArg: - - def __cinit__(self, key, value): - if isinstance(key, bytes): - self.key = key - elif isinstance(key, basestring): - self.key = key.encode() - else: - raise TypeError("expected key to be of type str or bytes") - if isinstance(value, bytes): - self.value = value - self.c_arg.type = grpc.GRPC_ARG_STRING - self.c_arg.value.string = self.value - elif isinstance(value, basestring): - self.value = value.encode() - self.c_arg.type = grpc.GRPC_ARG_STRING - self.c_arg.value.string = self.value - elif isinstance(value, int): - self.value = int(value) - self.c_arg.type = grpc.GRPC_ARG_INTEGER - self.c_arg.value.integer = self.value - else: - raise TypeError("expected value to be of type str or bytes or int") - self.c_arg.key = self.key - - -cdef class ChannelArgs: - - def __cinit__(self, args): - self.args = list(args) - for arg in self.args: - if not isinstance(arg, ChannelArg): - raise TypeError("expected list of ChannelArg") - self.c_args.arguments_length = len(self.args) - self.c_args.arguments = grpc.gpr_malloc( - self.c_args.arguments_length*sizeof(grpc.grpc_arg) - ) - for i in range(self.c_args.arguments_length): - self.c_args.arguments[i] = (self.args[i]).c_arg - - def __dealloc__(self): - grpc.gpr_free(self.c_args.arguments) - - def __len__(self): - # self.args is never stale; it's only updated from this file - return len(self.args) - - def __getitem__(self, size_t i): - # self.args is never stale; it's only updated from this file - return self.args[i] - - -cdef class Metadatum: - - def __cinit__(self, key, value): - if isinstance(key, bytes): - self._key = key - elif isinstance(key, basestring): - self._key = key.encode() - else: - raise TypeError("expected key to be of type str or bytes") - if isinstance(value, bytes): - self._value = value - elif isinstance(value, basestring): - self._value = value.encode() - else: - raise TypeError("expected value to be of type str or bytes") - self.c_metadata.key = self._key - self.c_metadata.value = self._value - self.c_metadata.value_length = len(self._value) - - @property - def key(self): - return self.c_metadata.key - - @property - def value(self): - return self.c_metadata.value[:self.c_metadata.value_length] - - def __len__(self): - return 2 - - def __getitem__(self, size_t i): - if i == 0: - return self.key - elif i == 1: - return self.value - else: - raise IndexError("index must be 0 (key) or 1 (value)") - - def __iter__(self): - return iter((self.key, self.value)) - - -cdef class _MetadataIterator: - - cdef size_t i - cdef Metadata metadata - - def __cinit__(self, Metadata metadata not None): - self.i = 0 - self.metadata = metadata - - def __next__(self): - if self.i < len(self.metadata): - result = self.metadata[self.i] - self.i = self.i + 1 - return result - else: - raise StopIteration() - - -cdef class Metadata: - - def __cinit__(self, metadata): - self.metadata = list(metadata) - for metadatum in metadata: - if not isinstance(metadatum, Metadatum): - raise TypeError("expected list of Metadatum") - grpc.grpc_metadata_array_init(&self.c_metadata_array) - self.c_metadata_array.count = len(self.metadata) - self.c_metadata_array.capacity = len(self.metadata) - self.c_metadata_array.metadata = grpc.gpr_malloc( - self.c_metadata_array.count*sizeof(grpc.grpc_metadata) - ) - for i in range(self.c_metadata_array.count): - self.c_metadata_array.metadata[i] = ( - (self.metadata[i]).c_metadata) - - def __dealloc__(self): - # this frees the allocated memory for the grpc_metadata_array (although - # it'd be nice if that were documented somewhere...) TODO(atash): document - # this in the C core - grpc.grpc_metadata_array_destroy(&self.c_metadata_array) - - def __len__(self): - return self.c_metadata_array.count - - def __getitem__(self, size_t i): - return Metadatum( - key=self.c_metadata_array.metadata[i].key, - value=self.c_metadata_array.metadata[i].value[ - :self.c_metadata_array.metadata[i].value_length]) - - def __iter__(self): - return _MetadataIterator(self) - - -cdef class Operation: - - def __cinit__(self): - self.references = [] - self._received_status_details = NULL - self._received_status_details_capacity = 0 - self.is_valid = False - - @property - def type(self): - return self.c_op.type - - @property - def received_message(self): - if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: - raise TypeError("self must be an operation receiving a message") - return self._received_message - - @property - def received_metadata(self): - if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and - self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): - raise TypeError("self must be an operation receiving metadata") - return self._received_metadata - - @property - def received_status_code(self): - if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: - raise TypeError("self must be an operation receiving a status code") - return self._received_status_code - - @property - def received_status_details(self): - if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: - raise TypeError("self must be an operation receiving status details") - if self._received_status_details: - return self._received_status_details - else: - return None - - @property - def received_cancelled(self): - if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: - raise TypeError("self must be an operation receiving cancellation " - "information") - return False if self._received_cancelled == 0 else True - - def __dealloc__(self): - # We *almost* don't need to do anything; most of the objects are handled by - # Python. The remaining one(s) are primitive fields filled in by GRPC core. - # This means that we need to clean up after receive_status_on_client. - if self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: - grpc.gpr_free(self._received_status_details) - -def operation_send_initial_metadata(Metadata metadata): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_SEND_INITIAL_METADATA - op.c_op.data.send_initial_metadata.count = metadata.c_metadata_array.count - op.c_op.data.send_initial_metadata.metadata = ( - metadata.c_metadata_array.metadata) - op.references.append(metadata) - op.is_valid = True - return op - -def operation_send_message(data): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_SEND_MESSAGE - byte_buffer = ByteBuffer(data) - op.c_op.data.send_message = byte_buffer.c_byte_buffer - op.references.append(byte_buffer) - op.is_valid = True - return op - -def operation_send_close_from_client(): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_SEND_CLOSE_FROM_CLIENT - op.is_valid = True - return op - -def operation_send_status_from_server( - Metadata metadata, grpc.grpc_status_code code, details): - if isinstance(details, bytes): - pass - elif isinstance(details, basestring): - details = details.encode() - else: - raise TypeError("expected a str or bytes object for details") - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_SEND_STATUS_FROM_SERVER - op.c_op.data.send_status_from_server.trailing_metadata_count = ( - metadata.c_metadata_array.count) - op.c_op.data.send_status_from_server.trailing_metadata = ( - metadata.c_metadata_array.metadata) - op.c_op.data.send_status_from_server.status = code - op.c_op.data.send_status_from_server.status_details = details - op.references.append(metadata) - op.references.append(details) - op.is_valid = True - return op - -def operation_receive_initial_metadata(): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_RECV_INITIAL_METADATA - op._received_metadata = Metadata([]) - op.c_op.data.receive_initial_metadata = ( - &op._received_metadata.c_metadata_array) - op.is_valid = True - return op - -def operation_receive_message(): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_RECV_MESSAGE - op._received_message = ByteBuffer(None) - # n.b. the c_op.data.receive_message field needs to be deleted by us, - # anyway, so we just let that be handled by the ByteBuffer() we allocated - # the line before. - op.c_op.data.receive_message = &op._received_message.c_byte_buffer - op.is_valid = True - return op - -def operation_receive_status_on_client(): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_RECV_STATUS_ON_CLIENT - op._received_metadata = Metadata([]) - op.c_op.data.receive_status_on_client.trailing_metadata = ( - &op._received_metadata.c_metadata_array) - op.c_op.data.receive_status_on_client.status = ( - &op._received_status_code) - op.c_op.data.receive_status_on_client.status_details = ( - &op._received_status_details) - op.c_op.data.receive_status_on_client.status_details_capacity = ( - &op._received_status_details_capacity) - op.is_valid = True - return op - -def operation_receive_close_on_server(): - cdef Operation op = Operation() - op.c_op.type = grpc.GRPC_OP_RECV_CLOSE_ON_SERVER - op.c_op.data.receive_close_on_server.cancelled = &op._received_cancelled - op.is_valid = True - return op - - -cdef class _OperationsIterator: - - cdef size_t i - cdef Operations operations - - def __cinit__(self, Operations operations not None): - self.i = 0 - self.operations = operations - - def __next__(self): - if self.i < len(self.operations): - result = self.operations[self.i] - self.i = self.i + 1 - return result - else: - raise StopIteration() - - -cdef class Operations: - - def __cinit__(self, operations): - self.operations = list(operations) # normalize iterable - self.c_ops = NULL - self.c_nops = 0 - for operation in self.operations: - if not isinstance(operation, Operation): - raise TypeError("expected operations to be iterable of Operation") - self.c_nops = len(self.operations) - self.c_ops = grpc.gpr_malloc( - sizeof(grpc.grpc_op)*self.c_nops) - for i in range(self.c_nops): - self.c_ops[i] = ((self.operations[i])).c_op - - def __len__(self): - return self.c_nops - - def __getitem__(self, size_t i): - # self.operations is never stale; it's only updated from this file - return self.operations[i] - - def __dealloc__(self): - grpc.gpr_free(self.c_ops) - - def __iter__(self): - return _OperationsIterator(self) - diff --git a/src/python/src/grpc/_cython/_cygrpc/server.pxd b/src/python/src/grpc/_cython/_cygrpc/server.pxd deleted file mode 100644 index 0257542a03..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/server.pxd +++ /dev/null @@ -1,45 +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. - -from grpc._cython._cygrpc cimport grpc -from grpc._cython._cygrpc cimport completion_queue - - -cdef class Server: - - cdef grpc.grpc_server *c_server - cdef bint is_started # start has been called - cdef bint is_shutting_down # shutdown has been called - cdef bint is_shutdown # notification of complete shutdown received - # used at dealloc when user forgets to shutdown - cdef completion_queue.CompletionQueue backup_shutdown_queue - cdef list references - cdef list registered_completion_queues - - cdef notify_shutdown_complete(self) diff --git a/src/python/src/grpc/_cython/_cygrpc/server.pyx b/src/python/src/grpc/_cython/_cygrpc/server.pyx deleted file mode 100644 index dcf9d38337..0000000000 --- a/src/python/src/grpc/_cython/_cygrpc/server.pyx +++ /dev/null @@ -1,158 +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. - -cimport cpython - -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport completion_queue -from grpc._cython._cygrpc cimport credentials -from grpc._cython._cygrpc cimport records - -import time - - -cdef class Server: - - def __cinit__(self, records.ChannelArgs arguments=None): - cdef grpc.grpc_channel_args *c_arguments = NULL - self.references = [] - self.registered_completion_queues = [] - if arguments is not None: - c_arguments = &arguments.c_args - self.references.append(arguments) - self.c_server = grpc.grpc_server_create(c_arguments) - self.is_started = False - self.is_shutting_down = False - self.is_shutdown = False - - def request_call( - self, completion_queue.CompletionQueue call_queue not None, - completion_queue.CompletionQueue server_queue not None, tag): - if not self.is_started or self.is_shutting_down: - raise ValueError("server must be started and not shutting down") - if server_queue not in self.registered_completion_queues: - raise ValueError("server_queue must be a registered completion queue") - cdef records.OperationTag operation_tag = records.OperationTag(tag) - operation_tag.operation_call = call.Call() - operation_tag.request_call_details = records.CallDetails() - operation_tag.request_metadata = records.Metadata([]) - operation_tag.references.extend([self, call_queue, server_queue]) - operation_tag.is_new_request = True - operation_tag.batch_operations = records.Operations([]) - cpython.Py_INCREF(operation_tag) - return grpc.grpc_server_request_call( - self.c_server, &operation_tag.operation_call.c_call, - &operation_tag.request_call_details.c_details, - &operation_tag.request_metadata.c_metadata_array, - call_queue.c_completion_queue, server_queue.c_completion_queue, - operation_tag) - - def register_completion_queue( - self, completion_queue.CompletionQueue queue not None): - if self.is_started: - raise ValueError("cannot register completion queues after start") - grpc.grpc_server_register_completion_queue( - self.c_server, queue.c_completion_queue) - self.registered_completion_queues.append(queue) - - def start(self): - if self.is_started: - raise ValueError("the server has already started") - self.backup_shutdown_queue = completion_queue.CompletionQueue() - self.register_completion_queue(self.backup_shutdown_queue) - self.is_started = True - grpc.grpc_server_start(self.c_server) - - def add_http2_port(self, address, - credentials.ServerCredentials server_credentials=None): - if isinstance(address, bytes): - pass - elif isinstance(address, basestring): - address = address.encode() - else: - raise TypeError("expected address to be a str or bytes") - self.references.append(address) - if server_credentials is not None: - self.references.append(server_credentials) - return grpc.grpc_server_add_secure_http2_port( - self.c_server, address, server_credentials.c_credentials) - else: - return grpc.grpc_server_add_http2_port(self.c_server, address) - - def shutdown(self, completion_queue.CompletionQueue queue not None, tag): - cdef records.OperationTag operation_tag - if queue.is_shutting_down: - raise ValueError("queue must be live") - elif not self.is_started: - raise ValueError("the server hasn't started yet") - elif self.is_shutting_down: - return - elif queue not in self.registered_completion_queues: - raise ValueError("expected registered completion queue") - else: - self.is_shutting_down = True - operation_tag = records.OperationTag(tag) - operation_tag.shutting_down_server = self - operation_tag.references.extend([self, queue]) - cpython.Py_INCREF(operation_tag) - grpc.grpc_server_shutdown_and_notify( - self.c_server, queue.c_completion_queue, - operation_tag) - - cdef notify_shutdown_complete(self): - # called only by a completion queue on receiving our shutdown operation tag - self.is_shutdown = True - - def cancel_all_calls(self): - if not self.is_shutting_down: - raise ValueError("the server must be shutting down to cancel all calls") - elif self.is_shutdown: - return - else: - grpc.grpc_server_cancel_all_calls(self.c_server) - - def __dealloc__(self): - if self.c_server != NULL: - if not self.is_started: - pass - elif self.is_shutdown: - pass - elif not self.is_shutting_down: - # the user didn't call shutdown - use our backup queue - self.shutdown(self.backup_shutdown_queue, None) - # and now we wait - while not self.is_shutdown: - self.backup_shutdown_queue.poll() - else: - # We're in the process of shutting down, but have not shutdown; can't do - # much but repeatedly release the GIL and wait - while not self.is_shutdown: - time.sleep(0) - grpc.grpc_server_destroy(self.c_server) - diff --git a/src/python/src/grpc/_cython/adapter_low.py b/src/python/src/grpc/_cython/adapter_low.py deleted file mode 100644 index 2bb468eece..0000000000 --- a/src/python/src/grpc/_cython/adapter_low.py +++ /dev/null @@ -1,106 +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. - - -# Adapter from grpc._cython.types to the surface expected by -# grpc._adapter._intermediary_low. -# -# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove -# both grpc._adapter._intermediary_low and this file. The fore and rear links in -# grpc._adapter should be able to use grpc._cython.types directly. - -from grpc._adapter import _types as type_interfaces -from grpc._cython import cygrpc - - -class ClientCredentials(object): - def __init__(self): - raise NotImplementedError() - - @staticmethod - def google_default(): - raise NotImplementedError() - - @staticmethod - def ssl(): - raise NotImplementedError() - - @staticmethod - def composite(): - raise NotImplementedError() - - @staticmethod - def compute_engine(): - raise NotImplementedError() - - @staticmethod - def service_account(): - raise NotImplementedError() - - @staticmethod - def jwt(): - raise NotImplementedError() - - @staticmethod - def refresh_token(): - raise NotImplementedError() - - @staticmethod - def iam(): - raise NotImplementedError() - - -class ServerCredentials(object): - def __init__(self): - raise NotImplementedError() - - @staticmethod - def ssl(): - raise NotImplementedError() - - -class CompletionQueue(type_interfaces.CompletionQueue): - def __init__(self): - raise NotImplementedError() - - -class Call(type_interfaces.Call): - def __init__(self): - raise NotImplementedError() - - -class Channel(type_interfaces.Channel): - def __init__(self): - raise NotImplementedError() - - -class Server(type_interfaces.Server): - def __init__(self): - raise NotImplementedError() - diff --git a/src/python/src/grpc/_cython/adapter_low_test.py b/src/python/src/grpc/_cython/adapter_low_test.py deleted file mode 100644 index 9bab930e56..0000000000 --- a/src/python/src/grpc/_cython/adapter_low_test.py +++ /dev/null @@ -1,187 +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. - -# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in -# grpc._cython.low should transparently support the semantics expected of -# grpc._adapter._low. - -import time -import unittest - -from grpc._adapter import _types -from grpc._cython import adapter_low as _low - - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue, []) - self.port = self.server.add_http2_port('[::]:0') - self.client_completion_queue = _low.CompletionQueue() - self.client_channel = _low.Channel('localhost:%d'%self.port, []) - - self.server.start() - - def tearDown(self): - self.server.shutdown() - del self.client_channel - - 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 - del self.server - - @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') - 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.assertEqual(_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.assertEqual(_types.CallError.OK, client_start_batch_result) - - request_event = self.server_completion_queue.next(DEADLINE) - self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) - self.assertIsInstance(request_event.call, _low.Call) - self.assertIs(server_request_tag, request_event.tag) - self.assertEqual(1, len(request_event.results)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.results[0].initial_metadata)) - self.assertEqual(METHOD, request_event.call_details.method) - self.assertEqual(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.assertEqual(_types.CallError.OK, server_start_batch_result) - - client_event = self.client_completion_queue.next(DEADLINE) - server_event = self.server_completion_queue.next(DEADLINE) - - self.assertEqual(6, len(client_event.results)) - found_client_op_types = set() - for client_result in client_event.results: - # we expect each op type to be unique - self.assertNotIn(client_result.type, found_client_op_types) - found_client_op_types.add(client_result.type) - if client_result.type == _types.OpType.RECV_INITIAL_METADATA: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.initial_metadata)) - elif client_result.type == _types.OpType.RECV_MESSAGE: - self.assertEqual(RESPONSE, client_result.message) - elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.trailing_metadata)) - self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) - self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) - self.assertEqual(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.assertEqual(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.assertEqual(REQUEST, server_result.message) - elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: - self.assertFalse(server_result.cancelled) - self.assertEqual(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__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_cython/cygrpc.pyx b/src/python/src/grpc/_cython/cygrpc.pyx deleted file mode 100644 index f4d9661580..0000000000 --- a/src/python/src/grpc/_cython/cygrpc.pyx +++ /dev/null @@ -1,107 +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. - -cimport cpython - -from grpc._cython._cygrpc cimport grpc -from grpc._cython._cygrpc cimport call -from grpc._cython._cygrpc cimport channel -from grpc._cython._cygrpc cimport credentials -from grpc._cython._cygrpc cimport completion_queue -from grpc._cython._cygrpc cimport records -from grpc._cython._cygrpc cimport server - -from grpc._cython._cygrpc import call -from grpc._cython._cygrpc import channel -from grpc._cython._cygrpc import credentials -from grpc._cython._cygrpc import completion_queue -from grpc._cython._cygrpc import records -from grpc._cython._cygrpc import server - -StatusCode = records.StatusCode -CallError = records.CallError -CompletionType = records.CompletionType -OperationType = records.OperationType -Timespec = records.Timespec -CallDetails = records.CallDetails -Event = records.Event -ByteBuffer = records.ByteBuffer -SslPemKeyCertPair = records.SslPemKeyCertPair -ChannelArg = records.ChannelArg -ChannelArgs = records.ChannelArgs -Metadatum = records.Metadatum -Metadata = records.Metadata -Operation = records.Operation - -operation_send_initial_metadata = records.operation_send_initial_metadata -operation_send_message = records.operation_send_message -operation_send_close_from_client = records.operation_send_close_from_client -operation_send_status_from_server = records.operation_send_status_from_server -operation_receive_initial_metadata = records.operation_receive_initial_metadata -operation_receive_message = records.operation_receive_message -operation_receive_status_on_client = records.operation_receive_status_on_client -operation_receive_close_on_server = records.operation_receive_close_on_server - -Operations = records.Operations - -ClientCredentials = credentials.ClientCredentials -ServerCredentials = credentials.ServerCredentials - -client_credentials_google_default = ( - credentials.client_credentials_google_default) -client_credentials_ssl = credentials.client_credentials_ssl -client_credentials_composite_credentials = ( - credentials.client_credentials_composite_credentials) -client_credentials_compute_engine = ( - credentials.client_credentials_compute_engine) -client_credentials_jwt = credentials.client_credentials_jwt -client_credentials_refresh_token = credentials.client_credentials_refresh_token -client_credentials_iam = credentials.client_credentials_iam -server_credentials_ssl = credentials.server_credentials_ssl - -CompletionQueue = completion_queue.CompletionQueue -Channel = channel.Channel -Server = server.Server -Call = call.Call - - -# -# Global state -# - -cdef class _ModuleState: - - def __cinit__(self): - grpc.grpc_init() - - def __dealloc__(self): - grpc.grpc_shutdown() - -_module_state = _ModuleState() - diff --git a/src/python/src/grpc/_cython/cygrpc_test.py b/src/python/src/grpc/_cython/cygrpc_test.py deleted file mode 100644 index 22d210b16b..0000000000 --- a/src/python/src/grpc/_cython/cygrpc_test.py +++ /dev/null @@ -1,262 +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. - -import time -import unittest - -from grpc._cython import cygrpc -from grpc._cython import test_utilities - - -class TypeSmokeTest(unittest.TestCase): - - def testStringsInUtilitiesUpDown(self): - self.assertEqual(0, cygrpc.StatusCode.ok) - metadatum = cygrpc.Metadatum('a', 'b') - self.assertEqual('a'.encode(), metadatum.key) - self.assertEqual('b'.encode(), metadatum.value) - metadata = cygrpc.Metadata([metadatum]) - self.assertEqual(1, len(metadata)) - self.assertEqual(metadatum.key, metadata[0].key) - - def testMetadataIteration(self): - metadata = cygrpc.Metadata([ - cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) - iterator = iter(metadata) - metadatum = next(iterator) - self.assertIsInstance(metadatum, cygrpc.Metadatum) - self.assertEqual(metadatum.key, 'a'.encode()) - self.assertEqual(metadatum.value, 'b'.encode()) - metadatum = next(iterator) - self.assertIsInstance(metadatum, cygrpc.Metadatum) - self.assertEqual(metadatum.key, 'c'.encode()) - self.assertEqual(metadatum.value, 'd'.encode()) - with self.assertRaises(StopIteration): - next(iterator) - - def testOperationsIteration(self): - operations = cygrpc.Operations([ - cygrpc.operation_send_message('asdf')]) - iterator = iter(operations) - operation = next(iterator) - self.assertIsInstance(operation, cygrpc.Operation) - # `Operation`s are write-only structures; can't directly debug anything out - # of them. Just check that we stop iterating. - with self.assertRaises(StopIteration): - next(iterator) - - def testTimespec(self): - now = time.time() - timespec = cygrpc.Timespec(now) - self.assertAlmostEqual(now, float(timespec), places=8) - - def testCompletionQueueUpDown(self): - completion_queue = cygrpc.CompletionQueue() - del completion_queue - - def testServerUpDown(self): - server = cygrpc.Server(cygrpc.ChannelArgs([])) - del server - - def testChannelUpDown(self): - channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) - del channel - - @unittest.skip('TODO(atash): undo skip after #2229 is merged') - def testServerStartNoExplicitShutdown(self): - server = cygrpc.Server() - completion_queue = cygrpc.CompletionQueue() - server.register_completion_queue(completion_queue) - port = server.add_http2_port('[::]:0') - self.assertIsInstance(port, int) - server.start() - del server - - @unittest.skip('TODO(atash): undo skip after #2229 is merged') - def testServerStartShutdown(self): - completion_queue = cygrpc.CompletionQueue() - server = cygrpc.Server() - server.add_http2_port('[::]:0') - server.register_completion_queue(completion_queue) - server.start() - shutdown_tag = object() - server.shutdown(completion_queue, shutdown_tag) - event = completion_queue.poll() - self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) - self.assertIs(shutdown_tag, event.tag) - del server - del completion_queue - - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = cygrpc.CompletionQueue() - self.server = cygrpc.Server() - self.server.register_completion_queue(self.server_completion_queue) - self.port = self.server.add_http2_port('[::]:0') - self.server.start() - self.client_completion_queue = cygrpc.CompletionQueue() - self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) - - def tearDown(self): - del self.server - 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 = b'key' - CLIENT_METADATA_ASCII_VALUE = b'val' - CLIENT_METADATA_BIN_KEY = b'key-bin' - CLIENT_METADATA_BIN_VALUE = b'\0'*1000 - SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' - SERVER_INITIAL_METADATA_VALUE = b'whodawha?' - SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' - SERVER_TRAILING_METADATA_VALUE = b'zomg it is' - SERVER_STATUS_CODE = cygrpc.StatusCode.ok - SERVER_STATUS_DETAILS = b'our work is never over' - REQUEST = b'in death a member of project mayhem has a name' - RESPONSE = b'his name is robert paulson' - METHOD = b'twinkies' - HOST = b'hostess' - - cygrpc_deadline = cygrpc.Timespec(DEADLINE) - - server_request_tag = object() - request_call_result = self.server.request_call( - self.server_completion_queue, self.server_completion_queue, - server_request_tag) - - self.assertEqual(cygrpc.CallError.ok, request_call_result) - - client_call_tag = object() - client_call = self.client_channel.create_call(self.client_completion_queue, - METHOD, HOST, cygrpc_deadline) - client_initial_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, - CLIENT_METADATA_ASCII_VALUE), - cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) - client_start_batch_result = client_call.start_batch(cygrpc.Operations([ - cygrpc.operation_send_initial_metadata(client_initial_metadata), - cygrpc.operation_send_message(REQUEST), - cygrpc.operation_send_close_from_client(), - cygrpc.operation_receive_initial_metadata(), - cygrpc.operation_receive_message(), - cygrpc.operation_receive_status_on_client() - ]), client_call_tag) - self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) - client_event_future = test_utilities.CompletionQueuePollFuture( - self.client_completion_queue, cygrpc_deadline) - - request_event = self.server_completion_queue.poll(cygrpc_deadline) - self.assertEqual(cygrpc.CompletionType.operation_complete, - request_event.type) - self.assertIsInstance(request_event.operation_call, cygrpc.Call) - self.assertIs(server_request_tag, request_event.tag) - self.assertEqual(0, len(request_event.batch_operations)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.request_metadata)) - self.assertEqual(METHOD, request_event.request_call_details.method) - self.assertEqual(HOST, request_event.request_call_details.host) - self.assertLess( - abs(DEADLINE - float(request_event.request_call_details.deadline)), - DEADLINE_TOLERANCE) - - server_call_tag = object() - server_call = request_event.operation_call - server_initial_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, - SERVER_INITIAL_METADATA_VALUE)]) - server_trailing_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, - SERVER_TRAILING_METADATA_VALUE)]) - server_start_batch_result = server_call.start_batch([ - cygrpc.operation_send_initial_metadata(server_initial_metadata), - cygrpc.operation_receive_message(), - cygrpc.operation_send_message(RESPONSE), - cygrpc.operation_receive_close_on_server(), - cygrpc.operation_send_status_from_server( - server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) - ], server_call_tag) - self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) - - client_event = client_event_future.result() - server_event = self.server_completion_queue.poll(cygrpc_deadline) - - self.assertEqual(6, len(client_event.batch_operations)) - found_client_op_types = set() - for client_result in client_event.batch_operations: - # we expect each op type to be unique - self.assertNotIn(client_result.type, found_client_op_types) - found_client_op_types.add(client_result.type) - if client_result.type == cygrpc.OperationType.receive_initial_metadata: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.received_metadata)) - elif client_result.type == cygrpc.OperationType.receive_message: - self.assertEqual(RESPONSE, client_result.received_message.bytes()) - elif client_result.type == cygrpc.OperationType.receive_status_on_client: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.received_metadata)) - self.assertEqual(SERVER_STATUS_DETAILS, - client_result.received_status_details) - self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) - self.assertEqual(set([ - cygrpc.OperationType.send_initial_metadata, - cygrpc.OperationType.send_message, - cygrpc.OperationType.send_close_from_client, - cygrpc.OperationType.receive_initial_metadata, - cygrpc.OperationType.receive_message, - cygrpc.OperationType.receive_status_on_client - ]), found_client_op_types) - - self.assertEqual(5, len(server_event.batch_operations)) - found_server_op_types = set() - for server_result in server_event.batch_operations: - self.assertNotIn(client_result.type, found_server_op_types) - found_server_op_types.add(server_result.type) - if server_result.type == cygrpc.OperationType.receive_message: - self.assertEqual(REQUEST, server_result.received_message.bytes()) - elif server_result.type == cygrpc.OperationType.receive_close_on_server: - self.assertFalse(server_result.received_cancelled) - self.assertEqual(set([ - cygrpc.OperationType.send_initial_metadata, - cygrpc.OperationType.receive_message, - cygrpc.OperationType.send_message, - cygrpc.OperationType.receive_close_on_server, - cygrpc.OperationType.send_status_from_server - ]), found_server_op_types) - - del client_call - del server_call - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_cython/test_utilities.py b/src/python/src/grpc/_cython/test_utilities.py deleted file mode 100644 index 21ea3075b4..0000000000 --- a/src/python/src/grpc/_cython/test_utilities.py +++ /dev/null @@ -1,46 +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. - -import threading - -from grpc._cython._cygrpc import completion_queue - - -class CompletionQueuePollFuture: - - def __init__(self, completion_queue, deadline): - def poller_function(): - self._event_result = completion_queue.poll(deadline) - self._event_result = None - self._thread = threading.Thread(target=poller_function) - self._thread.start() - - def result(self): - self._thread.join() - return self._event_result diff --git a/src/python/src/grpc/_junkdrawer/__init__.py b/src/python/src/grpc/_junkdrawer/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/_junkdrawer/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/_junkdrawer/math_pb2.py b/src/python/src/grpc/_junkdrawer/math_pb2.py deleted file mode 100644 index 20165955b4..0000000000 --- a/src/python/src/grpc/_junkdrawer/math_pb2.py +++ /dev/null @@ -1,266 +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. - -# TODO(nathaniel): Remove this from source control after having made -# generation from the math.proto source part of GRPC's build-and-test -# process. - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: math.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='math.proto', - package='math', - serialized_pb=_b('\n\nmath.proto\x12\x04math\",\n\x07\x44ivArgs\x12\x10\n\x08\x64ividend\x18\x01 \x02(\x03\x12\x0f\n\x07\x64ivisor\x18\x02 \x02(\x03\"/\n\x08\x44ivReply\x12\x10\n\x08quotient\x18\x01 \x02(\x03\x12\x11\n\tremainder\x18\x02 \x02(\x03\"\x18\n\x07\x46ibArgs\x12\r\n\x05limit\x18\x01 \x01(\x03\"\x12\n\x03Num\x12\x0b\n\x03num\x18\x01 \x02(\x03\"\x19\n\x08\x46ibReply\x12\r\n\x05\x63ount\x18\x01 \x02(\x03\x32\xa4\x01\n\x04Math\x12&\n\x03\x44iv\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00\x12.\n\x07\x44ivMany\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00(\x01\x30\x01\x12#\n\x03\x46ib\x12\r.math.FibArgs\x1a\t.math.Num\"\x00\x30\x01\x12\x1f\n\x03Sum\x12\t.math.Num\x1a\t.math.Num\"\x00(\x01') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_DIVARGS = _descriptor.Descriptor( - name='DivArgs', - full_name='math.DivArgs', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='dividend', full_name='math.DivArgs.dividend', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='divisor', full_name='math.DivArgs.divisor', index=1, - number=2, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=20, - serialized_end=64, -) - - -_DIVREPLY = _descriptor.Descriptor( - name='DivReply', - full_name='math.DivReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='quotient', full_name='math.DivReply.quotient', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='remainder', full_name='math.DivReply.remainder', index=1, - number=2, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=66, - serialized_end=113, -) - - -_FIBARGS = _descriptor.Descriptor( - name='FibArgs', - full_name='math.FibArgs', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='limit', full_name='math.FibArgs.limit', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=115, - serialized_end=139, -) - - -_NUM = _descriptor.Descriptor( - name='Num', - full_name='math.Num', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='num', full_name='math.Num.num', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=141, - serialized_end=159, -) - - -_FIBREPLY = _descriptor.Descriptor( - name='FibReply', - full_name='math.FibReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='count', full_name='math.FibReply.count', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=161, - serialized_end=186, -) - -DESCRIPTOR.message_types_by_name['DivArgs'] = _DIVARGS -DESCRIPTOR.message_types_by_name['DivReply'] = _DIVREPLY -DESCRIPTOR.message_types_by_name['FibArgs'] = _FIBARGS -DESCRIPTOR.message_types_by_name['Num'] = _NUM -DESCRIPTOR.message_types_by_name['FibReply'] = _FIBREPLY - -DivArgs = _reflection.GeneratedProtocolMessageType('DivArgs', (_message.Message,), dict( - DESCRIPTOR = _DIVARGS, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.DivArgs) - )) -_sym_db.RegisterMessage(DivArgs) - -DivReply = _reflection.GeneratedProtocolMessageType('DivReply', (_message.Message,), dict( - DESCRIPTOR = _DIVREPLY, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.DivReply) - )) -_sym_db.RegisterMessage(DivReply) - -FibArgs = _reflection.GeneratedProtocolMessageType('FibArgs', (_message.Message,), dict( - DESCRIPTOR = _FIBARGS, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.FibArgs) - )) -_sym_db.RegisterMessage(FibArgs) - -Num = _reflection.GeneratedProtocolMessageType('Num', (_message.Message,), dict( - DESCRIPTOR = _NUM, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.Num) - )) -_sym_db.RegisterMessage(Num) - -FibReply = _reflection.GeneratedProtocolMessageType('FibReply', (_message.Message,), dict( - DESCRIPTOR = _FIBREPLY, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.FibReply) - )) -_sym_db.RegisterMessage(FibReply) - - -# @@protoc_insertion_point(module_scope) diff --git a/src/python/src/grpc/_junkdrawer/stock_pb2.py b/src/python/src/grpc/_junkdrawer/stock_pb2.py deleted file mode 100644 index eef18f82d6..0000000000 --- a/src/python/src/grpc/_junkdrawer/stock_pb2.py +++ /dev/null @@ -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. - -# TODO(nathaniel): Remove this from source control after having made -# generation from the stock.proto source part of GRPC's build-and-test -# process. - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: stock.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='stock.proto', - package='stock', - serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_STOCKREQUEST = _descriptor.Descriptor( - name='StockRequest', - full_name='stock.StockRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='symbol', full_name='stock.StockRequest.symbol', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=True, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=22, - serialized_end=84, -) - - -_STOCKREPLY = _descriptor.Descriptor( - name='StockReply', - full_name='stock.StockReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='price', full_name='stock.StockReply.price', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='symbol', full_name='stock.StockReply.symbol', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=86, - serialized_end=129, -) - -DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST -DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY - -StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( - DESCRIPTOR = _STOCKREQUEST, - __module__ = 'stock_pb2' - # @@protoc_insertion_point(class_scope:stock.StockRequest) - )) -_sym_db.RegisterMessage(StockRequest) - -StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( - DESCRIPTOR = _STOCKREPLY, - __module__ = 'stock_pb2' - # @@protoc_insertion_point(class_scope:stock.StockReply) - )) -_sym_db.RegisterMessage(StockReply) - - -# @@protoc_insertion_point(module_scope) diff --git a/src/python/src/grpc/_links/__init__.py b/src/python/src/grpc/_links/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/_links/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/_links/_lonely_invocation_link_test.py b/src/python/src/grpc/_links/_lonely_invocation_link_test.py deleted file mode 100644 index 3d629f4387..0000000000 --- a/src/python/src/grpc/_links/_lonely_invocation_link_test.py +++ /dev/null @@ -1,88 +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. - -"""A test of invocation-side code unconnected to an RPC server.""" - -import unittest - -from grpc._adapter import _intermediary_low -from grpc._links import invocation -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_cases -from grpc.framework.interfaces.links import test_utilities - -_NULL_BEHAVIOR = lambda unused_argument: None - - -class LonelyInvocationLinkTest(unittest.TestCase): - - def testUpAndDown(self): - channel = _intermediary_low.Channel('nonexistent:54321', None) - invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {}) - - invocation_link.start() - invocation_link.stop() - - def _test_lonely_invocation_with_termination(self, termination): - test_operation_id = object() - test_group = 'test package.Test Service' - test_method = 'test method' - invocation_link_mate = test_utilities.RecordingLink() - - channel = _intermediary_low.Channel('nonexistent:54321', None) - invocation_link = invocation.invocation_link( - channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, - {(test_group, test_method): _NULL_BEHAVIOR}) - invocation_link.join_link(invocation_link_mate) - invocation_link.start() - - ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, - None, None, None, None, termination) - invocation_link.accept_ticket(ticket) - invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - - self.assertIsNot( - invocation_link_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - - def testLonelyInvocationLinkWithCommencementTicket(self): - self._test_lonely_invocation_with_termination(None) - - def testLonelyInvocationLinkWithEntireTicket(self): - self._test_lonely_invocation_with_termination( - links.Ticket.Termination.COMPLETION) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/python/src/grpc/_links/_proto_scenarios.py b/src/python/src/grpc/_links/_proto_scenarios.py deleted file mode 100644 index 320c0e0f50..0000000000 --- a/src/python/src/grpc/_links/_proto_scenarios.py +++ /dev/null @@ -1,261 +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. - -"""Test scenarios using protocol buffers.""" - -import abc -import threading - -from grpc._junkdrawer import math_pb2 -from grpc.framework.common import test_constants - - -class ProtoScenario(object): - """An RPC test scenario using protocol buffers.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def group_and_method(self): - """Access the test group and method. - - Returns: - The test group and method as a pair. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize a request protocol buffer. - - Args: - request: A request protocol buffer. - - Returns: - The bytestring serialization of the given request protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, request_bytestring): - """Deserialize a request protocol buffer. - - Args: - request_bytestring: The bytestring serialization of a request protocol - buffer. - - Returns: - The request protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize a response protocol buffer. - - Args: - response: A response protocol buffer. - - Returns: - The bytestring serialization of the given response protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, response_bytestring): - """Deserialize a response protocol buffer. - - Args: - response_bytestring: The bytestring serialization of a response protocol - buffer. - - Returns: - The response protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def requests(self): - """Access the sequence of requests for this scenario. - - Returns: - A sequence of request protocol buffers. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_for_request(self, request): - """Access the response for a particular request. - - Args: - request: A request protocol buffer. - - Returns: - The response protocol buffer appropriate for the given request. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_requests(self, experimental_requests): - """Verify the requests transmitted through the system under test. - - Args: - experimental_requests: The request protocol buffers transmitted through - the system under test. - - Returns: - True if the requests satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_responses(self, experimental_responses): - """Verify the responses transmitted through the system under test. - - Args: - experimental_responses: The response protocol buffers transmitted through - the system under test. - - Returns: - True if the responses satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - -class EmptyScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - def group_and_method(self): - return 'math.Math', 'DivMany' - - def serialize_request(self, request): - raise ValueError('This should not be necessary to call!') - - def deserialize_request(self, request_bytestring): - raise ValueError('This should not be necessary to call!') - - def serialize_response(self, response): - raise ValueError('This should not be necessary to call!') - - def deserialize_response(self, response_bytestring): - raise ValueError('This should not be necessary to call!') - - def requests(self): - return () - - def response_for_request(self, request): - raise ValueError('This should not be necessary to call!') - - def verify_requests(self, experimental_requests): - return not experimental_requests - - def verify_responses(self, experimental_responses): - return not experimental_responses - - -class BidirectionallyUnaryScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _DIVIDEND = 59 - _DIVISOR = 7 - _QUOTIENT = 8 - _REMAINDER = 3 - - _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) - _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) - - def group_and_method(self): - return 'math.Math', 'Div' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return [self._REQUEST] - - def response_for_request(self, request): - return self._RESPONSE - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == (self._REQUEST,) - - def verify_responses(self, experimental_responses): - return tuple(experimental_responses) == (self._RESPONSE,) - - -class BidirectionallyStreamingScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _REQUESTS = tuple( - math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) - for index in range(test_constants.STREAM_LENGTH)) - - def __init__(self): - self._lock = threading.Lock() - self._responses = [] - - def group_and_method(self): - return 'math.Math', 'DivMany' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return self._REQUESTS - - def response_for_request(self, request): - quotient, remainder = divmod(request.dividend, request.divisor) - response = math_pb2.DivReply(quotient=quotient, remainder=remainder) - with self._lock: - self._responses.append(response) - return response - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == self._REQUESTS - - def verify_responses(self, experimental_responses): - with self._lock: - return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/src/grpc/_links/_transmission_test.py b/src/python/src/grpc/_links/_transmission_test.py deleted file mode 100644 index 3eeec03f46..0000000000 --- a/src/python/src/grpc/_links/_transmission_test.py +++ /dev/null @@ -1,231 +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. - -"""Tests transmission of tickets across gRPC-on-the-wire.""" - -import unittest - -from grpc._adapter import _intermediary_low -from grpc._links import _proto_scenarios -from grpc._links import invocation -from grpc._links import service -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_cases -from grpc.framework.interfaces.links import test_utilities - -_IDENTITY = lambda x: x - - -class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): - - def create_transmitting_links(self): - service_link = service.service_link( - {self.group_and_method(): self.deserialize_request}, - {self.group_and_method(): self.serialize_response}) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', - {self.group_and_method(): self.serialize_request}, - {self.group_and_method(): self.deserialize_response}) - invocation_link.start() - return invocation_link, service_link - - def destroy_transmitting_links(self, invocation_side_link, service_side_link): - invocation_side_link.stop() - service_side_link.stop_gracefully() - - def create_invocation_initial_metadata(self): - return ( - ('first invocation initial metadata key', 'just a string value'), - ('second invocation initial metadata key', '0123456789'), - ('third invocation initial metadata key-bin', '\x00\x57' * 100), - ) - - def create_invocation_terminal_metadata(self): - return None - - def create_service_initial_metadata(self): - return ( - ('first service initial metadata key', 'just another string value'), - ('second service initial metadata key', '9876543210'), - ('third service initial metadata key-bin', '\x00\x59\x02' * 100), - ) - - def create_service_terminal_metadata(self): - return ( - ('first service terminal metadata key', 'yet another string value'), - ('second service terminal metadata key', 'abcdefghij'), - ('third service terminal metadata key-bin', '\x00\x37' * 100), - ) - - def create_invocation_completion(self): - return None, None - - def create_service_completion(self): - return _intermediary_low.Code.OK, 'An exuberant test "details" message!' - - def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): - # we need to filter out any additional metadata added in transmitted_metadata - # since implementations are allowed to add to what is sent (in any position) - keys, _ = zip(*original_metadata) - self.assertSequenceEqual( - original_metadata, - [x for x in transmitted_metadata if x[0] in keys]) - - -class RoundTripTest(unittest.TestCase): - - def testZeroMessageRoundTrip(self): - test_operation_id = object() - test_group = 'test package.Test Group' - test_method = 'test method' - identity_transformation = {(test_group, test_method): _IDENTITY} - test_code = _intermediary_low.Code.OK - test_message = 'a test message' - - service_link = service.service_link( - identity_transformation, identity_transformation) - service_mate = test_utilities.RecordingLink() - service_link.join_link(service_mate) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', identity_transformation, identity_transformation) - invocation_mate = test_utilities.RecordingLink() - invocation_link.join_link(invocation_mate) - invocation_link.start() - - invocation_ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, - None, None, None, None, links.Ticket.Termination.COMPLETION) - invocation_link.accept_ticket(invocation_ticket) - service_mate.block_until_tickets_satisfy(test_cases.terminated) - - service_ticket = links.Ticket( - service_mate.tickets()[-1].operation_id, 0, None, None, None, None, - None, None, None, None, test_code, test_message, - links.Ticket.Termination.COMPLETION) - service_link.accept_ticket(service_ticket) - invocation_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - service_link.stop_gracefully() - - self.assertIs( - service_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - self.assertIs( - invocation_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - - def _perform_scenario_test(self, scenario): - test_operation_id = object() - test_group, test_method = scenario.group_and_method() - test_code = _intermediary_low.Code.OK - test_message = 'a scenario test message' - - service_link = service.service_link( - {(test_group, test_method): scenario.deserialize_request}, - {(test_group, test_method): scenario.serialize_response}) - service_mate = test_utilities.RecordingLink() - service_link.join_link(service_mate) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', - {(test_group, test_method): scenario.serialize_request}, - {(test_group, test_method): scenario.deserialize_response}) - invocation_mate = test_utilities.RecordingLink() - invocation_link.join_link(invocation_mate) - invocation_link.start() - - invocation_ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, - None, None, None, None, None) - invocation_link.accept_ticket(invocation_ticket) - requests = scenario.requests() - for request_index, request in enumerate(requests): - request_ticket = links.Ticket( - test_operation_id, 1 + request_index, None, None, None, None, 1, None, - request, None, None, None, None) - invocation_link.accept_ticket(request_ticket) - service_mate.block_until_tickets_satisfy( - test_cases.at_least_n_payloads_received_predicate(1 + request_index)) - response_ticket = links.Ticket( - service_mate.tickets()[0].operation_id, request_index, None, None, - None, None, 1, None, scenario.response_for_request(request), None, - None, None, None) - service_link.accept_ticket(response_ticket) - invocation_mate.block_until_tickets_satisfy( - test_cases.at_least_n_payloads_received_predicate(1 + request_index)) - request_count = len(requests) - invocation_completion_ticket = links.Ticket( - test_operation_id, request_count + 1, None, None, None, None, None, - None, None, None, None, None, links.Ticket.Termination.COMPLETION) - invocation_link.accept_ticket(invocation_completion_ticket) - service_mate.block_until_tickets_satisfy(test_cases.terminated) - service_completion_ticket = links.Ticket( - service_mate.tickets()[0].operation_id, request_count, None, None, None, - None, None, None, None, None, test_code, test_message, - links.Ticket.Termination.COMPLETION) - service_link.accept_ticket(service_completion_ticket) - invocation_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - service_link.stop_gracefully() - - observed_requests = tuple( - ticket.payload for ticket in service_mate.tickets() - if ticket.payload is not None) - observed_responses = tuple( - ticket.payload for ticket in invocation_mate.tickets() - if ticket.payload is not None) - self.assertTrue(scenario.verify_requests(observed_requests)) - self.assertTrue(scenario.verify_responses(observed_responses)) - - def testEmptyScenario(self): - self._perform_scenario_test(_proto_scenarios.EmptyScenario()) - - def testBidirectionallyUnaryScenario(self): - self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) - - def testBidirectionallyStreamingScenario(self): - self._perform_scenario_test( - _proto_scenarios.BidirectionallyStreamingScenario()) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/_links/invocation.py b/src/python/src/grpc/_links/invocation.py deleted file mode 100644 index 0058ae91f8..0000000000 --- a/src/python/src/grpc/_links/invocation.py +++ /dev/null @@ -1,363 +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. - -"""The RPC-invocation-side bridge between RPC Framework and GRPC-on-the-wire.""" - -import abc -import enum -import logging -import threading -import time - -from grpc._adapter import _intermediary_low -from grpc.framework.foundation import activated -from grpc.framework.foundation import logging_pool -from grpc.framework.foundation import relay -from grpc.framework.interfaces.links import links - - -@enum.unique -class _Read(enum.Enum): - AWAITING_METADATA = 'awaiting metadata' - READING = 'reading' - AWAITING_ALLOWANCE = 'awaiting allowance' - CLOSED = 'closed' - - -@enum.unique -class _HighWrite(enum.Enum): - OPEN = 'open' - CLOSED = 'closed' - - -@enum.unique -class _LowWrite(enum.Enum): - OPEN = 'OPEN' - ACTIVE = 'ACTIVE' - CLOSED = 'CLOSED' - - -class _RPCState(object): - - def __init__( - self, call, request_serializer, response_deserializer, sequence_number, - read, allowance, high_write, low_write): - self.call = call - self.request_serializer = request_serializer - self.response_deserializer = response_deserializer - self.sequence_number = sequence_number - self.read = read - self.allowance = allowance - self.high_write = high_write - self.low_write = low_write - - -class _Kernel(object): - - def __init__( - self, channel, host, request_serializers, response_deserializers, - ticket_relay): - self._lock = threading.Lock() - self._channel = channel - self._host = host - self._request_serializers = request_serializers - self._response_deserializers = response_deserializers - self._relay = ticket_relay - - self._completion_queue = None - self._rpc_states = None - self._pool = None - - def _on_write_event(self, operation_id, unused_event, rpc_state): - if rpc_state.high_write is _HighWrite.CLOSED: - rpc_state.call.complete(operation_id) - rpc_state.low_write = _LowWrite.CLOSED - else: - ticket = links.Ticket( - operation_id, rpc_state.sequence_number, None, None, None, None, 1, - None, None, None, None, None, None) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - rpc_state.low_write = _LowWrite.OPEN - - def _on_read_event(self, operation_id, event, rpc_state): - if event.bytes is None: - rpc_state.read = _Read.CLOSED - else: - if 0 < rpc_state.allowance: - rpc_state.allowance -= 1 - rpc_state.call.read(operation_id) - else: - rpc_state.read = _Read.AWAITING_ALLOWANCE - ticket = links.Ticket( - operation_id, rpc_state.sequence_number, None, None, None, None, None, - None, rpc_state.response_deserializer(event.bytes), None, None, None, - None) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - - def _on_metadata_event(self, operation_id, event, rpc_state): - rpc_state.allowance -= 1 - rpc_state.call.read(operation_id) - rpc_state.read = _Read.READING - ticket = links.Ticket( - operation_id, rpc_state.sequence_number, None, None, - links.Ticket.Subscription.FULL, None, None, event.metadata, None, None, - None, None, None) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - - def _on_finish_event(self, operation_id, event, rpc_state): - self._rpc_states.pop(operation_id, None) - if event.status.code is _intermediary_low.Code.OK: - termination = links.Ticket.Termination.COMPLETION - elif event.status.code is _intermediary_low.Code.CANCELLED: - termination = links.Ticket.Termination.CANCELLATION - elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: - termination = links.Ticket.Termination.EXPIRATION - else: - termination = links.Ticket.Termination.TRANSMISSION_FAILURE - ticket = links.Ticket( - operation_id, rpc_state.sequence_number, None, None, None, None, None, - None, None, event.metadata, event.status.code, event.status.details, - termination) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - - def _spin(self, completion_queue): - while True: - event = completion_queue.get(None) - if event.kind is _intermediary_low.Event.Kind.STOP: - return - operation_id = event.tag - with self._lock: - if self._completion_queue is None: - continue - rpc_state = self._rpc_states.get(operation_id) - if rpc_state is not None: - if event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: - self._on_write_event(operation_id, event, rpc_state) - elif event.kind is _intermediary_low.Event.Kind.METADATA_ACCEPTED: - self._on_metadata_event(operation_id, event, rpc_state) - elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: - self._on_read_event(operation_id, event, rpc_state) - elif event.kind is _intermediary_low.Event.Kind.FINISH: - self._on_finish_event(operation_id, event, rpc_state) - elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: - pass - else: - logging.error('Illegal RPC event! %s', (event,)) - - def _invoke( - self, operation_id, group, method, initial_metadata, payload, termination, - timeout, allowance): - """Invoke an RPC. - - Args: - operation_id: Any object to be used as an operation ID for the RPC. - group: The group to which the RPC method belongs. - method: The RPC method name. - initial_metadata: The initial metadata object for the RPC. - payload: A payload object for the RPC or None if no payload was given at - invocation-time. - termination: A links.Ticket.Termination value or None indicated whether or - not more writes will follow from this side of the RPC. - timeout: A duration of time in seconds to allow for the RPC. - allowance: The number of payloads (beyond the free first one) that the - local ticket exchange mate has granted permission to be read. - """ - if termination is links.Ticket.Termination.COMPLETION: - high_write = _HighWrite.CLOSED - elif termination is None: - high_write = _HighWrite.OPEN - else: - return - - request_serializer = self._request_serializers.get((group, method)) - response_deserializer = self._response_deserializers.get((group, method)) - if request_serializer is None or response_deserializer is None: - cancellation_ticket = links.Ticket( - operation_id, 0, None, None, None, None, None, None, None, None, None, - None, links.Ticket.Termination.CANCELLATION) - self._relay.add_value(cancellation_ticket) - return - - call = _intermediary_low.Call( - self._channel, self._completion_queue, '/%s/%s' % (group, method), - self._host, time.time() + timeout) - if initial_metadata is not None: - for metadata_key, metadata_value in initial_metadata: - call.add_metadata(metadata_key, metadata_value) - call.invoke(self._completion_queue, operation_id, operation_id) - if payload is None: - if high_write is _HighWrite.CLOSED: - call.complete(operation_id) - low_write = _LowWrite.CLOSED - else: - low_write = _LowWrite.OPEN - else: - call.write(request_serializer(payload), operation_id) - low_write = _LowWrite.ACTIVE - self._rpc_states[operation_id] = _RPCState( - call, request_serializer, response_deserializer, 0, - _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), - high_write, low_write) - - def _advance(self, operation_id, rpc_state, payload, termination, allowance): - if payload is not None: - rpc_state.call.write(rpc_state.request_serializer(payload), operation_id) - rpc_state.low_write = _LowWrite.ACTIVE - - if allowance is not None: - if rpc_state.read is _Read.AWAITING_ALLOWANCE: - rpc_state.allowance += allowance - 1 - rpc_state.call.read(operation_id) - rpc_state.read = _Read.READING - else: - rpc_state.allowance += allowance - - if termination is links.Ticket.Termination.COMPLETION: - rpc_state.high_write = _HighWrite.CLOSED - if rpc_state.low_write is _LowWrite.OPEN: - rpc_state.call.complete(operation_id) - rpc_state.low_write = _LowWrite.CLOSED - elif termination is not None: - rpc_state.call.cancel() - - def add_ticket(self, ticket): - with self._lock: - if self._completion_queue is None: - return - if ticket.sequence_number == 0: - self._invoke( - ticket.operation_id, ticket.group, ticket.method, - ticket.initial_metadata, ticket.payload, ticket.termination, - ticket.timeout, ticket.allowance) - else: - rpc_state = self._rpc_states.get(ticket.operation_id) - if rpc_state is not None: - self._advance( - ticket.operation_id, rpc_state, ticket.payload, - ticket.termination, ticket.allowance) - - def start(self): - """Starts this object. - - This method must be called before attempting to exchange tickets with this - object. - """ - with self._lock: - self._completion_queue = _intermediary_low.CompletionQueue() - self._rpc_states = {} - self._pool = logging_pool.pool(1) - self._pool.submit(self._spin, self._completion_queue) - - def stop(self): - """Stops this object. - - This method must be called for proper termination of this object, and no - attempts to exchange tickets with this object may be made after this method - has been called. - """ - with self._lock: - self._completion_queue.stop() - self._completion_queue = None - pool = self._pool - self._pool = None - self._rpc_states = None - pool.shutdown(wait=True) - - -class InvocationLink(links.Link, activated.Activated): - """A links.Link for use on the invocation-side of a gRPC connection. - - Implementations of this interface are only valid for use when activated. - """ - __metaclass__ = abc.ABCMeta - - -class _InvocationLink(InvocationLink): - - def __init__( - self, channel, host, request_serializers, response_deserializers): - self._relay = relay.relay(None) - self._kernel = _Kernel( - channel, host, request_serializers, response_deserializers, self._relay) - - def _start(self): - self._relay.start() - self._kernel.start() - return self - - def _stop(self): - self._kernel.stop() - self._relay.stop() - - def accept_ticket(self, ticket): - """See links.Link.accept_ticket for specification.""" - self._kernel.add_ticket(ticket) - - def join_link(self, link): - """See links.Link.join_link for specification.""" - self._relay.set_behavior(link.accept_ticket) - - def __enter__(self): - """See activated.Activated.__enter__ for specification.""" - return self._start() - - def __exit__(self, exc_type, exc_val, exc_tb): - """See activated.Activated.__exit__ for specification.""" - self._stop() - return False - - def start(self): - """See activated.Activated.start for specification.""" - return self._start() - - def stop(self): - """See activated.Activated.stop for specification.""" - self._stop() - - -def invocation_link(channel, host, request_serializers, response_deserializers): - """Creates an InvocationLink. - - Args: - channel: A channel for use by the link. - host: The host to specify when invoking RPCs. - request_serializers: A dict from group-method pair to request object - serialization behavior. - response_deserializers: A dict from group-method pair to response object - deserialization behavior. - - Returns: - An InvocationLink. - """ - return _InvocationLink( - channel, host, request_serializers, response_deserializers) diff --git a/src/python/src/grpc/_links/service.py b/src/python/src/grpc/_links/service.py deleted file mode 100644 index 7783e91824..0000000000 --- a/src/python/src/grpc/_links/service.py +++ /dev/null @@ -1,402 +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. - -"""The RPC-service-side bridge between RPC Framework and GRPC-on-the-wire.""" - -import abc -import enum -import logging -import threading -import time - -from grpc._adapter import _intermediary_low -from grpc.framework.foundation import logging_pool -from grpc.framework.foundation import relay -from grpc.framework.interfaces.links import links - - -@enum.unique -class _Read(enum.Enum): - READING = 'reading' - AWAITING_ALLOWANCE = 'awaiting allowance' - CLOSED = 'closed' - - -@enum.unique -class _HighWrite(enum.Enum): - OPEN = 'open' - CLOSED = 'closed' - - -@enum.unique -class _LowWrite(enum.Enum): - """The possible categories of low-level write state.""" - - OPEN = 'OPEN' - ACTIVE = 'ACTIVE' - CLOSED = 'CLOSED' - - -class _RPCState(object): - - def __init__( - self, request_deserializer, response_serializer, sequence_number, read, - allowance, high_write, low_write, premetadataed, terminal_metadata, code, - message): - self.request_deserializer = request_deserializer - self.response_serializer = response_serializer - self.sequence_number = sequence_number - self.read = read - self.allowance = allowance - self.high_write = high_write - self.low_write = low_write - self.premetadataed = premetadataed - self.terminal_metadata = terminal_metadata - self.code = code - self.message = message - - -def _metadatafy(call, metadata): - for metadata_key, metadata_value in metadata: - call.add_metadata(metadata_key, metadata_value) - - -class _Kernel(object): - - def __init__(self, request_deserializers, response_serializers, ticket_relay): - self._lock = threading.Lock() - self._request_deserializers = request_deserializers - self._response_serializers = response_serializers - self._relay = ticket_relay - - self._completion_queue = None - self._server = None - self._rpc_states = {} - self._pool = None - - def _on_service_acceptance_event(self, event, server): - server.service(None) - - service_acceptance = event.service_acceptance - call = service_acceptance.call - call.accept(self._completion_queue, call) - try: - group, method = service_acceptance.method.split('/')[1:3] - except ValueError: - logging.info('Illegal path "%s"!', service_acceptance.method) - return - request_deserializer = self._request_deserializers.get((group, method)) - response_serializer = self._response_serializers.get((group, method)) - if request_deserializer is None or response_serializer is None: - # TODO(nathaniel): Terminate the RPC with code NOT_FOUND. - call.cancel() - return - - call.read(call) - self._rpc_states[call] = _RPCState( - request_deserializer, response_serializer, 1, _Read.READING, 0, - _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None) - ticket = links.Ticket( - call, 0, group, method, links.Ticket.Subscription.FULL, - service_acceptance.deadline - time.time(), None, event.metadata, None, - None, None, None, None) - self._relay.add_value(ticket) - - def _on_read_event(self, event): - call = event.tag - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - if event.bytes is None: - rpc_state.read = _Read.CLOSED - payload = None - termination = links.Ticket.Termination.COMPLETION - else: - if 0 < rpc_state.allowance: - rpc_state.allowance -= 1 - call.read(call) - else: - rpc_state.read = _Read.AWAITING_ALLOWANCE - payload = rpc_state.request_deserializer(event.bytes) - termination = None - ticket = links.Ticket( - call, rpc_state.sequence_number, None, None, None, None, None, None, - payload, None, None, None, termination) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - - def _on_write_event(self, event): - call = event.tag - rpc_state = self._rpc_states.get(call, None) - if rpc_state is None: - return - - if rpc_state.high_write is _HighWrite.CLOSED: - if rpc_state.terminal_metadata is not None: - _metadatafy(call, rpc_state.terminal_metadata) - call.status( - _intermediary_low.Status(rpc_state.code, rpc_state.message), call) - rpc_state.low_write = _LowWrite.CLOSED - else: - ticket = links.Ticket( - call, rpc_state.sequence_number, None, None, None, None, 1, None, - None, None, None, None, None) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - rpc_state.low_write = _LowWrite.OPEN - - def _on_finish_event(self, event): - call = event.tag - rpc_state = self._rpc_states.pop(call, None) - if rpc_state is None: - return - code = event.status.code - if code is _intermediary_low.Code.OK: - return - - if code is _intermediary_low.Code.CANCELLED: - termination = links.Ticket.Termination.CANCELLATION - elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: - termination = links.Ticket.Termination.EXPIRATION - else: - termination = links.Ticket.Termination.TRANSMISSION_FAILURE - ticket = links.Ticket( - call, rpc_state.sequence_number, None, None, None, None, None, None, - None, None, None, None, termination) - rpc_state.sequence_number += 1 - self._relay.add_value(ticket) - - def _spin(self, completion_queue, server): - while True: - event = completion_queue.get(None) - if event.kind is _intermediary_low.Event.Kind.STOP: - return - with self._lock: - if self._server is None: - continue - elif event.kind is _intermediary_low.Event.Kind.SERVICE_ACCEPTED: - self._on_service_acceptance_event(event, server) - elif event.kind is _intermediary_low.Event.Kind.READ_ACCEPTED: - self._on_read_event(event) - elif event.kind is _intermediary_low.Event.Kind.WRITE_ACCEPTED: - self._on_write_event(event) - elif event.kind is _intermediary_low.Event.Kind.COMPLETE_ACCEPTED: - pass - elif event.kind is _intermediary_low.Event.Kind.FINISH: - self._on_finish_event(event) - else: - logging.error('Illegal event! %s', (event,)) - - def add_ticket(self, ticket): - with self._lock: - if self._server is None: - return - call = ticket.operation_id - rpc_state = self._rpc_states.get(call) - if rpc_state is None: - return - - if ticket.initial_metadata is not None: - _metadatafy(call, ticket.initial_metadata) - call.premetadata() - rpc_state.premetadataed = True - elif not rpc_state.premetadataed: - if (ticket.terminal_metadata is not None or - ticket.payload is not None or - ticket.termination is links.Ticket.Termination.COMPLETION or - ticket.code is not None or - ticket.message is not None): - call.premetadata() - rpc_state.premetadataed = True - - if ticket.allowance is not None: - if rpc_state.read is _Read.AWAITING_ALLOWANCE: - rpc_state.allowance += ticket.allowance - 1 - call.read(call) - rpc_state.read = _Read.READING - else: - rpc_state.allowance += ticket.allowance - - if ticket.payload is not None: - call.write(rpc_state.response_serializer(ticket.payload), call) - rpc_state.low_write = _LowWrite.ACTIVE - - if ticket.terminal_metadata is not None: - rpc_state.terminal_metadata = ticket.terminal_metadata - if ticket.code is not None: - rpc_state.code = ticket.code - if ticket.message is not None: - rpc_state.message = ticket.message - - if ticket.termination is links.Ticket.Termination.COMPLETION: - rpc_state.high_write = _HighWrite.CLOSED - if rpc_state.low_write is _LowWrite.OPEN: - if rpc_state.terminal_metadata is not None: - _metadatafy(call, rpc_state.terminal_metadata) - status = _intermediary_low.Status( - _intermediary_low.Code.OK - if rpc_state.code is None else rpc_state.code, - '' if rpc_state.message is None else rpc_state.message) - call.status(status, call) - rpc_state.low_write = _LowWrite.CLOSED - elif ticket.termination is not None: - call.cancel() - self._rpc_states.pop(call, None) - - def add_port(self, port, server_credentials): - with self._lock: - address = '[::]:%d' % port - if self._server is None: - self._completion_queue = _intermediary_low.CompletionQueue() - self._server = _intermediary_low.Server(self._completion_queue) - if server_credentials is None: - return self._server.add_http2_addr(address) - else: - return self._server.add_secure_http2_addr(address, server_credentials) - - def start(self): - with self._lock: - if self._server is None: - self._completion_queue = _intermediary_low.CompletionQueue() - self._server = _intermediary_low.Server(self._completion_queue) - self._pool = logging_pool.pool(1) - self._pool.submit(self._spin, self._completion_queue, self._server) - self._server.start() - self._server.service(None) - - def graceful_stop(self): - with self._lock: - self._server.stop() - self._server = None - self._completion_queue.stop() - self._completion_queue = None - pool = self._pool - self._pool = None - self._rpc_states = None - pool.shutdown(wait=True) - - def immediate_stop(self): - # TODO(nathaniel): Implementation. - raise NotImplementedError( - 'TODO(nathaniel): after merge of rewritten lower layers') - - -class ServiceLink(links.Link): - """A links.Link for use on the service-side of a gRPC connection. - - Implementations of this interface are only valid for use between calls to - their start method and one of their stop methods. - """ - - @abc.abstractmethod - def add_port(self, port, server_credentials): - """Adds a port on which to service RPCs after this link has been started. - - Args: - port: The port on which to service RPCs, or zero to request that a port be - automatically selected and used. - server_credentials: A ServerCredentials object, or None for insecure - service. - - Returns: - A port on which RPCs will be serviced after this link has been started. - """ - raise NotImplementedError() - - @abc.abstractmethod - def start(self): - """Starts this object. - - This method must be called before attempting to use this Link in ticket - exchange. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stop_gracefully(self): - """Stops this link. - - New RPCs will be rejected as soon as this method is called, but ongoing RPCs - will be allowed to continue until they terminate. This method blocks until - all RPCs have terminated. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stop_immediately(self): - """Stops this link. - - All in-progress RPCs will be terminated immediately. - """ - raise NotImplementedError() - - -class _ServiceLink(ServiceLink): - - def __init__(self, request_deserializers, response_serializers): - self._relay = relay.relay(None) - self._kernel = _Kernel( - request_deserializers, response_serializers, self._relay) - - def accept_ticket(self, ticket): - self._kernel.add_ticket(ticket) - - def join_link(self, link): - self._relay.set_behavior(link.accept_ticket) - - def add_port(self, port, server_credentials): - return self._kernel.add_port(port, server_credentials) - - def start(self): - self._relay.start() - return self._kernel.start() - - def stop_gracefully(self): - self._kernel.graceful_stop() - self._relay.stop() - - def stop_immediately(self): - self._kernel.immediate_stop() - self._relay.stop() - - -def service_link(request_deserializers, response_serializers): - """Creates a ServiceLink. - - Args: - request_deserializers: A dict from group-method pair to request object - deserialization behavior. - response_serializers: A dict from group-method pair to response ojbect - serialization behavior. - - Returns: - A ServiceLink. - """ - return _ServiceLink(request_deserializers, response_serializers) diff --git a/src/python/src/grpc/early_adopter/__init__.py b/src/python/src/grpc/early_adopter/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/early_adopter/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/early_adopter/implementations.py b/src/python/src/grpc/early_adopter/implementations.py deleted file mode 100644 index 10919fae69..0000000000 --- a/src/python/src/grpc/early_adopter/implementations.py +++ /dev/null @@ -1,250 +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. - -"""Entry points into GRPC.""" - -import threading - -from grpc._adapter import fore as _fore -from grpc._adapter import rear as _rear -from grpc.framework.alpha import _face_utilities -from grpc.framework.alpha import _reexport -from grpc.framework.alpha import interfaces -from grpc.framework.base import implementations as _base_implementations -from grpc.framework.base import util as _base_utilities -from grpc.framework.face import implementations as _face_implementations -from grpc.framework.foundation import logging_pool - -_THREAD_POOL_SIZE = 8 -_ONE_DAY_IN_SECONDS = 24 * 60 * 60 - - -class _Server(interfaces.Server): - - def __init__(self, breakdown, port, private_key, certificate_chain): - self._lock = threading.Lock() - self._breakdown = breakdown - self._port = port - if private_key is None or certificate_chain is None: - self._key_chain_pairs = () - else: - self._key_chain_pairs = ((private_key, certificate_chain),) - - self._pool = None - self._back = None - self._fore_link = None - - def _start(self): - with self._lock: - if self._pool is None: - self._pool = logging_pool.pool(_THREAD_POOL_SIZE) - servicer = _face_implementations.servicer( - self._pool, self._breakdown.implementations, None) - self._back = _base_implementations.back_link( - servicer, self._pool, self._pool, self._pool, _ONE_DAY_IN_SECONDS, - _ONE_DAY_IN_SECONDS) - self._fore_link = _fore.ForeLink( - self._pool, self._breakdown.request_deserializers, - self._breakdown.response_serializers, None, self._key_chain_pairs, - port=self._port) - self._back.join_fore_link(self._fore_link) - self._fore_link.join_rear_link(self._back) - self._fore_link.start() - else: - raise ValueError('Server currently running!') - - def _stop(self): - with self._lock: - if self._pool is None: - raise ValueError('Server not running!') - else: - self._fore_link.stop() - _base_utilities.wait_for_idle(self._back) - self._pool.shutdown(wait=True) - self._fore_link = None - self._back = None - self._pool = None - - def __enter__(self): - self._start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self._stop() - return False - - def start(self): - self._start() - - def stop(self): - self._stop() - - def port(self): - with self._lock: - return self._fore_link.port() - - -class _Stub(interfaces.Stub): - - def __init__( - self, breakdown, host, port, secure, root_certificates, private_key, - certificate_chain, metadata_transformer=None, server_host_override=None): - self._lock = threading.Lock() - self._breakdown = breakdown - self._host = host - self._port = port - self._secure = secure - self._root_certificates = root_certificates - self._private_key = private_key - self._certificate_chain = certificate_chain - self._metadata_transformer = metadata_transformer - self._server_host_override = server_host_override - - self._pool = None - self._front = None - self._rear_link = None - self._understub = None - - def __enter__(self): - with self._lock: - if self._pool is None: - self._pool = logging_pool.pool(_THREAD_POOL_SIZE) - self._front = _base_implementations.front_link( - self._pool, self._pool, self._pool) - self._rear_link = _rear.RearLink( - self._host, self._port, self._pool, - self._breakdown.request_serializers, - self._breakdown.response_deserializers, self._secure, - self._root_certificates, self._private_key, self._certificate_chain, - metadata_transformer=self._metadata_transformer, - server_host_override=self._server_host_override) - self._front.join_rear_link(self._rear_link) - self._rear_link.join_fore_link(self._front) - self._rear_link.start() - self._understub = _face_implementations.dynamic_stub( - self._breakdown.face_cardinalities, self._front, self._pool, '') - else: - raise ValueError('Tried to __enter__ already-__enter__ed Stub!') - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - with self._lock: - if self._pool is None: - raise ValueError('Tried to __exit__ non-__enter__ed Stub!') - else: - self._rear_link.stop() - _base_utilities.wait_for_idle(self._front) - self._pool.shutdown(wait=True) - self._rear_link = None - self._front = None - self._pool = None - self._understub = None - return False - - def __getattr__(self, attr): - with self._lock: - if self._pool is None: - raise ValueError('Tried to __getattr__ non-__enter__ed Stub!') - else: - method_cardinality = self._breakdown.cardinalities.get(attr) - underlying_attr = getattr( - self._understub, self._breakdown.qualified_names.get(attr), None) - if method_cardinality is interfaces.Cardinality.UNARY_UNARY: - return _reexport.unary_unary_sync_async(underlying_attr) - elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: - return lambda request, timeout: _reexport.cancellable_iterator( - underlying_attr(request, timeout)) - elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: - return _reexport.stream_unary_sync_async(underlying_attr) - elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: - return lambda request_iterator, timeout: ( - _reexport.cancellable_iterator(underlying_attr( - request_iterator, timeout))) - else: - raise AttributeError(attr) - - -def stub( - service_name, methods, host, port, metadata_transformer=None, secure=False, - root_certificates=None, private_key=None, certificate_chain=None, - server_host_override=None): - """Constructs an interfaces.Stub. - - Args: - service_name: The package-qualified full name of the service. - methods: A dictionary from RPC method name to - interfaces.RpcMethodInvocationDescription describing the RPCs to be - supported by the created stub. The RPC method names in the dictionary are - not qualified by the service name or decorated in any other way. - host: The host to which to connect for RPC service. - port: The port to which to connect for RPC service. - metadata_transformer: A callable that given a metadata object produces - another metadata object to be used in the underlying communication on the - wire. - secure: Whether or not to construct the stub with a secure connection. - root_certificates: The PEM-encoded root certificates or None to ask for - them to be retrieved from a default location. - private_key: The PEM-encoded private key to use or None if no private key - should be used. - certificate_chain: The PEM-encoded certificate chain to use or None if no - certificate chain should be used. - server_host_override: (For testing only) the target name used for SSL - host name checking. - - Returns: - An interfaces.Stub affording RPC invocation. - """ - breakdown = _face_utilities.break_down_invocation(service_name, methods) - return _Stub( - breakdown, host, port, secure, root_certificates, private_key, - certificate_chain, server_host_override=server_host_override, - metadata_transformer=metadata_transformer) - - -def server( - service_name, methods, port, private_key=None, certificate_chain=None): - """Constructs an interfaces.Server. - - Args: - service_name: The package-qualified full name of the service. - methods: A dictionary from RPC method name to - interfaces.RpcMethodServiceDescription describing the RPCs to - be serviced by the created server. The RPC method names in the dictionary - are not qualified by the service name or decorated in any other way. - port: The port on which to serve or zero to ask for a port to be - automatically selected. - private_key: A pem-encoded private key, or None for an insecure server. - certificate_chain: A pem-encoded certificate chain, or None for an insecure - server. - - Returns: - An interfaces.Server that will serve secure traffic. - """ - breakdown = _face_utilities.break_down_service(service_name, methods) - return _Server(breakdown, port, private_key, certificate_chain) diff --git a/src/python/src/grpc/early_adopter/implementations_test.py b/src/python/src/grpc/early_adopter/implementations_test.py deleted file mode 100644 index 49f0e949c4..0000000000 --- a/src/python/src/grpc/early_adopter/implementations_test.py +++ /dev/null @@ -1,180 +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. - -# TODO(nathaniel): Expand this test coverage. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import unittest - -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -from grpc._junkdrawer import math_pb2 - -SERVICE_NAME = 'math.Math' - -DIV = 'Div' -DIV_MANY = 'DivMany' -FIB = 'Fib' -SUM = 'Sum' - -def _fibbonacci(limit): - left, right = 0, 1 - for _ in xrange(limit): - yield left - left, right = right, left + right - - -def _div(request, unused_context): - return math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _div_many(request_iterator, unused_context): - for request in request_iterator: - yield math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _fib(request, unused_context): - for number in _fibbonacci(request.limit): - yield math_pb2.Num(num=number) - - -def _sum(request_iterator, unused_context): - accumulation = 0 - for request in request_iterator: - accumulation += request.num - return math_pb2.Num(num=accumulation) - - -_INVOCATION_DESCRIPTIONS = { - DIV: utilities.unary_unary_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - DIV_MANY: utilities.stream_stream_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - FIB: utilities.unary_stream_invocation_description( - math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), - SUM: utilities.stream_unary_invocation_description( - math_pb2.Num.SerializeToString, math_pb2.Num.FromString), -} - -_SERVICE_DESCRIPTIONS = { - DIV: utilities.unary_unary_service_description( - _div, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - DIV_MANY: utilities.stream_stream_service_description( - _div_many, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - FIB: utilities.unary_stream_service_description( - _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), - SUM: utilities.stream_unary_service_description( - _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), -} - -_TIMEOUT = 3 - - -class EarlyAdopterImplementationsTest(unittest.TestCase): - - def setUp(self): - self.server = implementations.server( - SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) - self.server.start() - port = self.server.port() - self.stub = implementations.stub( - SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) - - def tearDown(self): - self.server.stop() - - def testUpAndDown(self): - with self.stub: - pass - - def testUnaryUnary(self): - divisor = 59 - dividend = 973 - expected_quotient = dividend / divisor - expected_remainder = dividend % divisor - - with self.stub: - response = self.stub.Div( - math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) - self.assertEqual(expected_quotient, response.quotient) - self.assertEqual(expected_remainder, response.remainder) - - def testUnaryStream(self): - stream_length = 43 - - with self.stub: - response_iterator = self.stub.Fib( - math_pb2.FibArgs(limit=stream_length), _TIMEOUT) - numbers = tuple(response.num for response in response_iterator) - for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): - self.assertEqual(early + middle, later) - self.assertEqual(stream_length, len(numbers)) - - def testStreamUnary(self): - stream_length = 127 - - with self.stub: - response_future = self.stub.Sum.async( - (math_pb2.Num(num=index) for index in range(stream_length)), - _TIMEOUT) - self.assertEqual( - (stream_length * (stream_length - 1)) / 2, - response_future.result().num) - - def testStreamStream(self): - stream_length = 179 - divisor_offset = 71 - dividend_offset = 1763 - - with self.stub: - response_iterator = self.stub.DivMany( - (math_pb2.DivArgs( - divisor=divisor_offset + index, - dividend=dividend_offset + index) - for index in range(stream_length)), - _TIMEOUT) - for index, response in enumerate(response_iterator): - self.assertEqual( - (dividend_offset + index) / (divisor_offset + index), - response.quotient) - self.assertEqual( - (dividend_offset + index) % (divisor_offset + index), - response.remainder) - self.assertEqual(stream_length, index + 1) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/__init__.py b/src/python/src/grpc/framework/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/alpha/__init__.py b/src/python/src/grpc/framework/alpha/__init__.py deleted file mode 100644 index b89398809f..0000000000 --- a/src/python/src/grpc/framework/alpha/__init__.py +++ /dev/null @@ -1,28 +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. diff --git a/src/python/src/grpc/framework/alpha/_face_utilities.py b/src/python/src/grpc/framework/alpha/_face_utilities.py deleted file mode 100644 index fb0cfe426d..0000000000 --- a/src/python/src/grpc/framework/alpha/_face_utilities.py +++ /dev/null @@ -1,183 +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. - -import abc -import collections - -# face_interfaces is referenced from specification in this module. -from grpc.framework.common import cardinality -from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc.framework.face import utilities as face_utilities -from grpc.framework.alpha import _reexport -from grpc.framework.alpha import interfaces - - -def _qualified_name(service_name, method_name): - return '/%s/%s' % (service_name, method_name) - - -# TODO(nathaniel): This structure is getting bloated; it could be shrunk if -# implementations._Stub used a generic rather than a dynamic underlying -# face-layer stub. -class InvocationBreakdown(object): - """An intermediate representation of invocation-side views of RPC methods. - - Attributes: - cardinalities: A dictionary from RPC method name to interfaces.Cardinality - value. - qualified_names: A dictionary from unqualified RPC method name to - service-qualified RPC method name. - face_cardinalities: A dictionary from service-qualified RPC method name to - to cardinality.Cardinality value. - request_serializers: A dictionary from service-qualified RPC method name to - callable behavior to be used serializing request values for the RPC. - response_deserializers: A dictionary from service-qualified RPC method name - to callable behavior to be used deserializing response values for the - RPC. - """ - __metaclass__ = abc.ABCMeta - - -class _EasyInvocationBreakdown( - InvocationBreakdown, - collections.namedtuple( - '_EasyInvocationBreakdown', - ('cardinalities', 'qualified_names', 'face_cardinalities', - 'request_serializers', 'response_deserializers'))): - pass - - -class ServiceBreakdown(object): - """An intermediate representation of service-side views of RPC methods. - - Attributes: - implementations: A dictionary from service-qualified RPC method name to - face_interfaces.MethodImplementation implementing the RPC method. - request_deserializers: A dictionary from service-qualified RPC method name - to callable behavior to be used deserializing request values for the RPC. - response_serializers: A dictionary from service-qualified RPC method name - to callable behavior to be used serializing response values for the RPC. - """ - __metaclass__ = abc.ABCMeta - - -class _EasyServiceBreakdown( - ServiceBreakdown, - collections.namedtuple( - '_EasyServiceBreakdown', - ('implementations', 'request_deserializers', 'response_serializers'))): - pass - - -def break_down_invocation(service_name, method_descriptions): - """Derives an InvocationBreakdown from several RPC method descriptions. - - Args: - service_name: The package-qualified full name of the service. - method_descriptions: A dictionary from RPC method name to - interfaces.RpcMethodInvocationDescription describing the RPCs. - - Returns: - An InvocationBreakdown corresponding to the given method descriptions. - """ - cardinalities = {} - qualified_names = {} - face_cardinalities = {} - request_serializers = {} - response_deserializers = {} - for name, method_description in method_descriptions.iteritems(): - qualified_name = _qualified_name(service_name, name) - method_cardinality = method_description.cardinality() - cardinalities[name] = method_description.cardinality() - qualified_names[name] = qualified_name - face_cardinalities[qualified_name] = _reexport.common_cardinality( - method_cardinality) - request_serializers[qualified_name] = method_description.serialize_request - response_deserializers[qualified_name] = ( - method_description.deserialize_response) - return _EasyInvocationBreakdown( - cardinalities, qualified_names, face_cardinalities, request_serializers, - response_deserializers) - - -def break_down_service(service_name, method_descriptions): - """Derives a ServiceBreakdown from several RPC method descriptions. - - Args: - method_descriptions: A dictionary from RPC method name to - interfaces.RpcMethodServiceDescription describing the RPCs. - - Returns: - A ServiceBreakdown corresponding to the given method descriptions. - """ - implementations = {} - request_deserializers = {} - response_serializers = {} - for name, method_description in method_descriptions.iteritems(): - qualified_name = _qualified_name(service_name, name) - method_cardinality = method_description.cardinality() - if method_cardinality is interfaces.Cardinality.UNARY_UNARY: - def service( - request, face_rpc_context, - service_behavior=method_description.service_unary_unary): - return service_behavior( - request, _reexport.rpc_context(face_rpc_context)) - implementations[qualified_name] = face_utilities.unary_unary_inline( - service) - elif method_cardinality is interfaces.Cardinality.UNARY_STREAM: - def service( - request, face_rpc_context, - service_behavior=method_description.service_unary_stream): - return service_behavior( - request, _reexport.rpc_context(face_rpc_context)) - implementations[qualified_name] = face_utilities.unary_stream_inline( - service) - elif method_cardinality is interfaces.Cardinality.STREAM_UNARY: - def service( - request_iterator, face_rpc_context, - service_behavior=method_description.service_stream_unary): - return service_behavior( - request_iterator, _reexport.rpc_context(face_rpc_context)) - implementations[qualified_name] = face_utilities.stream_unary_inline( - service) - elif method_cardinality is interfaces.Cardinality.STREAM_STREAM: - def service( - request_iterator, face_rpc_context, - service_behavior=method_description.service_stream_stream): - return service_behavior( - request_iterator, _reexport.rpc_context(face_rpc_context)) - implementations[qualified_name] = face_utilities.stream_stream_inline( - service) - request_deserializers[qualified_name] = ( - method_description.deserialize_request) - response_serializers[qualified_name] = ( - method_description.serialize_response) - - return _EasyServiceBreakdown( - implementations, request_deserializers, response_serializers) diff --git a/src/python/src/grpc/framework/alpha/_reexport.py b/src/python/src/grpc/framework/alpha/_reexport.py deleted file mode 100644 index 198cb95ad5..0000000000 --- a/src/python/src/grpc/framework/alpha/_reexport.py +++ /dev/null @@ -1,203 +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. - -from grpc.framework.common import cardinality -from grpc.framework.face import exceptions as face_exceptions -from grpc.framework.face import interfaces as face_interfaces -from grpc.framework.foundation import future -from grpc.framework.alpha import exceptions -from grpc.framework.alpha import interfaces - -_EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY = { - interfaces.Cardinality.UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, - interfaces.Cardinality.UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, - interfaces.Cardinality.STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, - interfaces.Cardinality.STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, -} - -_ABORTION_REEXPORT = { - face_interfaces.Abortion.CANCELLED: interfaces.Abortion.CANCELLED, - face_interfaces.Abortion.EXPIRED: interfaces.Abortion.EXPIRED, - face_interfaces.Abortion.NETWORK_FAILURE: - interfaces.Abortion.NETWORK_FAILURE, - face_interfaces.Abortion.SERVICED_FAILURE: - interfaces.Abortion.SERVICED_FAILURE, - face_interfaces.Abortion.SERVICER_FAILURE: - interfaces.Abortion.SERVICER_FAILURE, -} - - -class _RpcError(exceptions.RpcError): - pass - - -def _reexport_error(face_rpc_error): - if isinstance(face_rpc_error, face_exceptions.CancellationError): - return exceptions.CancellationError() - elif isinstance(face_rpc_error, face_exceptions.ExpirationError): - return exceptions.ExpirationError() - else: - return _RpcError() - - -def _as_face_abortion_callback(abortion_callback): - def face_abortion_callback(face_abortion): - abortion_callback(_ABORTION_REEXPORT[face_abortion]) - return face_abortion_callback - - -class _ReexportedFuture(future.Future): - - def __init__(self, face_future): - self._face_future = face_future - - def cancel(self): - return self._face_future.cancel() - - def cancelled(self): - return self._face_future.cancelled() - - def running(self): - return self._face_future.running() - - def done(self): - return self._face_future.done() - - def result(self, timeout=None): - try: - return self._face_future.result(timeout=timeout) - except face_exceptions.RpcError as e: - raise _reexport_error(e) - - def exception(self, timeout=None): - face_error = self._face_future.exception(timeout=timeout) - return None if face_error is None else _reexport_error(face_error) - - def traceback(self, timeout=None): - return self._face_future.traceback(timeout=timeout) - - def add_done_callback(self, fn): - self._face_future.add_done_callback(lambda unused_face_future: fn(self)) - - -def _call_reexporting_errors(behavior, *args, **kwargs): - try: - return behavior(*args, **kwargs) - except face_exceptions.RpcError as e: - raise _reexport_error(e) - - -def _reexported_future(face_future): - return _ReexportedFuture(face_future) - - -class _CancellableIterator(interfaces.CancellableIterator): - - def __init__(self, face_cancellable_iterator): - self._face_cancellable_iterator = face_cancellable_iterator - - def __iter__(self): - return self - - def next(self): - return _call_reexporting_errors(self._face_cancellable_iterator.next) - - def cancel(self): - self._face_cancellable_iterator.cancel() - - -class _RpcContext(interfaces.RpcContext): - - def __init__(self, face_rpc_context): - self._face_rpc_context = face_rpc_context - - def is_active(self): - return self._face_rpc_context.is_active() - - def time_remaining(self): - return self._face_rpc_context.time_remaining() - - def add_abortion_callback(self, abortion_callback): - self._face_rpc_context.add_abortion_callback( - _as_face_abortion_callback(abortion_callback)) - - -class _UnaryUnarySyncAsync(interfaces.UnaryUnarySyncAsync): - - def __init__(self, face_unary_unary_multi_callable): - self._underlying = face_unary_unary_multi_callable - - def __call__(self, request, timeout): - return _call_reexporting_errors( - self._underlying, request, timeout) - - def async(self, request, timeout): - return _ReexportedFuture(self._underlying.future(request, timeout)) - - -class _StreamUnarySyncAsync(interfaces.StreamUnarySyncAsync): - - def __init__(self, face_stream_unary_multi_callable): - self._underlying = face_stream_unary_multi_callable - - def __call__(self, request_iterator, timeout): - return _call_reexporting_errors( - self._underlying, request_iterator, timeout) - - def async(self, request_iterator, timeout): - return _ReexportedFuture(self._underlying.future(request_iterator, timeout)) - - -def common_cardinality(early_adopter_cardinality): - return _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ - early_adopter_cardinality] - - -def common_cardinalities(early_adopter_cardinalities): - common_cardinalities = {} - for name, early_adopter_cardinality in early_adopter_cardinalities.iteritems(): - common_cardinalities[name] = _EARLY_ADOPTER_CARDINALITY_TO_COMMON_CARDINALITY[ - early_adopter_cardinality] - return common_cardinalities - - -def rpc_context(face_rpc_context): - return _RpcContext(face_rpc_context) - - -def cancellable_iterator(face_cancellable_iterator): - return _CancellableIterator(face_cancellable_iterator) - - -def unary_unary_sync_async(face_unary_unary_multi_callable): - return _UnaryUnarySyncAsync(face_unary_unary_multi_callable) - - -def stream_unary_sync_async(face_stream_unary_multi_callable): - return _StreamUnarySyncAsync(face_stream_unary_multi_callable) diff --git a/src/python/src/grpc/framework/alpha/exceptions.py b/src/python/src/grpc/framework/alpha/exceptions.py deleted file mode 100644 index 5234d3b91c..0000000000 --- a/src/python/src/grpc/framework/alpha/exceptions.py +++ /dev/null @@ -1,48 +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. - -"""Exceptions raised by GRPC. - -Only GRPC should instantiate and raise these exceptions. -""" - -import abc - - -class RpcError(Exception): - """Common super type for all exceptions raised by GRPC.""" - __metaclass__ = abc.ABCMeta - - -class CancellationError(RpcError): - """Indicates that an RPC has been cancelled.""" - - -class ExpirationError(RpcError): - """Indicates that an RPC has expired ("timed out").""" diff --git a/src/python/src/grpc/framework/alpha/interfaces.py b/src/python/src/grpc/framework/alpha/interfaces.py deleted file mode 100644 index 8380567c97..0000000000 --- a/src/python/src/grpc/framework/alpha/interfaces.py +++ /dev/null @@ -1,388 +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. - -"""Interfaces of GRPC.""" - -import abc -import enum - -# exceptions is referenced from specification in this module. -from grpc.framework.alpha import exceptions # pylint: disable=unused-import -from grpc.framework.foundation import activated -from grpc.framework.foundation import future - - -@enum.unique -class Cardinality(enum.Enum): - """Constants for the four cardinalities of RPC.""" - - UNARY_UNARY = 'request-unary/response-unary' - UNARY_STREAM = 'request-unary/response-streaming' - STREAM_UNARY = 'request-streaming/response-unary' - STREAM_STREAM = 'request-streaming/response-streaming' - - -@enum.unique -class Abortion(enum.Enum): - """Categories of RPC abortion.""" - - CANCELLED = 'cancelled' - EXPIRED = 'expired' - NETWORK_FAILURE = 'network failure' - SERVICED_FAILURE = 'serviced failure' - SERVICER_FAILURE = 'servicer failure' - - -class CancellableIterator(object): - """Implements the Iterator protocol and affords a cancel method.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __iter__(self): - """Returns the self object in accordance with the Iterator protocol.""" - raise NotImplementedError() - - @abc.abstractmethod - def next(self): - """Returns a value or raises StopIteration per the Iterator protocol.""" - raise NotImplementedError() - - @abc.abstractmethod - def cancel(self): - """Requests cancellation of whatever computation underlies this iterator.""" - raise NotImplementedError() - - -class RpcContext(object): - """Provides RPC-related information and action.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def is_active(self): - """Describes whether the RPC is active or has terminated.""" - raise NotImplementedError() - - @abc.abstractmethod - def time_remaining(self): - """Describes the length of allowed time remaining for the RPC. - Returns: - A nonnegative float indicating the length of allowed time in seconds - remaining for the RPC to complete before it is considered to have timed - out. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_abortion_callback(self, abortion_callback): - """Registers a callback to be called if the RPC is aborted. - Args: - abortion_callback: A callable to be called and passed an Abortion value - in the event of RPC abortion. - """ - raise NotImplementedError() - - -class UnaryUnarySyncAsync(object): - """Affords invoking a unary-unary RPC synchronously or asynchronously. - Values implementing this interface are directly callable and present an - "async" method. Both calls take a request value and a numeric timeout. - Direct invocation of a value of this type invokes its associated RPC and - blocks until the RPC's response is available. Calling the "async" method - of a value of this type invokes its associated RPC and immediately returns a - future.Future bound to the asynchronous execution of the RPC. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request, timeout): - """Synchronously invokes the underlying RPC. - Args: - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - Returns: - The response value for the RPC. - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def async(self, request, timeout): - """Asynchronously invokes the underlying RPC. - Args: - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future's result value will be the response value of the RPC. - In the event of RPC abortion, the returned Future's exception value - will be an exceptions.RpcError. - """ - raise NotImplementedError() - - -class StreamUnarySyncAsync(object): - """Affords invoking a stream-unary RPC synchronously or asynchronously. - Values implementing this interface are directly callable and present an - "async" method. Both calls take an iterator of request values and a numeric - timeout. Direct invocation of a value of this type invokes its associated RPC - and blocks until the RPC's response is available. Calling the "async" method - of a value of this type invokes its associated RPC and immediately returns a - future.Future bound to the asynchronous execution of the RPC. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request_iterator, timeout): - """Synchronously invokes the underlying RPC. - - Args: - request_iterator: An iterator that yields request values for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - The response value for the RPC. - - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def async(self, request_iterator, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request_iterator: An iterator that yields request values for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future's result value will be the response value of the RPC. - In the event of RPC abortion, the returned Future's exception value - will be an exceptions.RpcError. - """ - raise NotImplementedError() - - -class RpcMethodDescription(object): - """A type for the common aspects of RPC method descriptions.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def cardinality(self): - """Identifies the cardinality of this RpcMethodDescription. - - Returns: - A Cardinality value identifying whether or not this - RpcMethodDescription is request-unary or request-streaming and - whether or not it is response-unary or response-streaming. - """ - raise NotImplementedError() - - -class RpcMethodInvocationDescription(RpcMethodDescription): - """Invocation-side description of an RPC method.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def serialize_request(self, request): - """Serializes a request value. - - Args: - request: A request value appropriate for the RPC method described by this - RpcMethodInvocationDescription. - - Returns: - The serialization of the given request value as a - bytestring. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, serialized_response): - """Deserializes a response value. - - Args: - serialized_response: A bytestring that is the serialization of a response - value appropriate for the RPC method described by this - RpcMethodInvocationDescription. - - Returns: - A response value corresponding to the given bytestring. - """ - raise NotImplementedError() - - -class RpcMethodServiceDescription(RpcMethodDescription): - """Service-side description of an RPC method.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def deserialize_request(self, serialized_request): - """Deserializes a request value. - - Args: - serialized_request: A bytestring that is the serialization of a request - value appropriate for the RPC method described by this - RpcMethodServiceDescription. - - Returns: - A request value corresponding to the given bytestring. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serializes a response value. - - Args: - response: A response value appropriate for the RPC method described by - this RpcMethodServiceDescription. - - Returns: - The serialization of the given response value as a - bytestring. - """ - raise NotImplementedError() - - @abc.abstractmethod - def service_unary_unary(self, request, context): - """Carries out this RPC. - - This method may only be called if the cardinality of this - RpcMethodServiceDescription is Cardinality.UNARY_UNARY. - - Args: - request: A request value appropriate for the RPC method described by this - RpcMethodServiceDescription. - context: An RpcContext object for the RPC. - - Returns: - A response value appropriate for the RPC method described by this - RpcMethodServiceDescription. - """ - raise NotImplementedError() - - @abc.abstractmethod - def service_unary_stream(self, request, context): - """Carries out this RPC. - - This method may only be called if the cardinality of this - RpcMethodServiceDescription is Cardinality.UNARY_STREAM. - - Args: - request: A request value appropriate for the RPC method described by this - RpcMethodServiceDescription. - context: An RpcContext object for the RPC. - - Yields: - Zero or more response values appropriate for the RPC method described by - this RpcMethodServiceDescription. - """ - raise NotImplementedError() - - @abc.abstractmethod - def service_stream_unary(self, request_iterator, context): - """Carries out this RPC. - - This method may only be called if the cardinality of this - RpcMethodServiceDescription is Cardinality.STREAM_UNARY. - - Args: - request_iterator: An iterator of request values appropriate for the RPC - method described by this RpcMethodServiceDescription. - context: An RpcContext object for the RPC. - - Returns: - A response value appropriate for the RPC method described by this - RpcMethodServiceDescription. - """ - raise NotImplementedError() - - @abc.abstractmethod - def service_stream_stream(self, request_iterator, context): - """Carries out this RPC. - - This method may only be called if the cardinality of this - RpcMethodServiceDescription is Cardinality.STREAM_STREAM. - - Args: - request_iterator: An iterator of request values appropriate for the RPC - method described by this RpcMethodServiceDescription. - context: An RpcContext object for the RPC. - - Yields: - Zero or more response values appropriate for the RPC method described by - this RpcMethodServiceDescription. - """ - raise NotImplementedError() - - -class Stub(object): - """A stub with callable RPC method names for attributes. - - Instances of this type are context managers and only afford RPC invocation - when used in context. - - Instances of this type, when used in context, respond to attribute access - as follows: if the requested attribute is the name of a unary-unary RPC - method, the value of the attribute will be a UnaryUnarySyncAsync with which - to invoke the RPC method. If the requested attribute is the name of a - unary-stream RPC method, the value of the attribute will be a callable taking - a request object and a timeout parameter and returning a CancellableIterator - that yields the response values of the RPC. If the requested attribute is the - name of a stream-unary RPC method, the value of the attribute will be a - StreamUnarySyncAsync with which to invoke the RPC method. If the requested - attribute is the name of a stream-stream RPC method, the value of the - attribute will be a callable taking an iterator of request objects and a - timeout and returning a CancellableIterator that yields the response values - of the RPC. - - In all cases indication of abortion is indicated by raising of - exceptions.RpcError, exceptions.CancellationError, - and exceptions.ExpirationError. - """ - __metaclass__ = abc.ABCMeta - - -class Server(activated.Activated): - """A GRPC Server.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def port(self): - """Reports the port on which the server is serving. - - This method may only be called while the server is activated. - - Returns: - The port on which the server is serving. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/alpha/utilities.py b/src/python/src/grpc/framework/alpha/utilities.py deleted file mode 100644 index 7d7f78f5e4..0000000000 --- a/src/python/src/grpc/framework/alpha/utilities.py +++ /dev/null @@ -1,269 +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. - -"""Utilities for use with GRPC.""" - -from grpc.framework.alpha import interfaces - - -class _RpcMethodDescription( - interfaces.RpcMethodInvocationDescription, - interfaces.RpcMethodServiceDescription): - - def __init__( - self, cardinality, unary_unary, unary_stream, stream_unary, - stream_stream, request_serializer, request_deserializer, - response_serializer, response_deserializer): - self._cardinality = cardinality - self._unary_unary = unary_unary - self._unary_stream = unary_stream - self._stream_unary = stream_unary - self._stream_stream = stream_stream - self._request_serializer = request_serializer - self._request_deserializer = request_deserializer - self._response_serializer = response_serializer - self._response_deserializer = response_deserializer - - def cardinality(self): - """See interfaces.RpcMethodDescription.cardinality for specification.""" - return self._cardinality - - def serialize_request(self, request): - """See interfaces.RpcMethodInvocationDescription.serialize_request.""" - return self._request_serializer(request) - - def deserialize_request(self, serialized_request): - """See interfaces.RpcMethodServiceDescription.deserialize_request.""" - return self._request_deserializer(serialized_request) - - def serialize_response(self, response): - """See interfaces.RpcMethodServiceDescription.serialize_response.""" - return self._response_serializer(response) - - def deserialize_response(self, serialized_response): - """See interfaces.RpcMethodInvocationDescription.deserialize_response.""" - return self._response_deserializer(serialized_response) - - def service_unary_unary(self, request, context): - """See interfaces.RpcMethodServiceDescription.service_unary_unary.""" - return self._unary_unary(request, context) - - def service_unary_stream(self, request, context): - """See interfaces.RpcMethodServiceDescription.service_unary_stream.""" - return self._unary_stream(request, context) - - def service_stream_unary(self, request_iterator, context): - """See interfaces.RpcMethodServiceDescription.service_stream_unary.""" - return self._stream_unary(request_iterator, context) - - def service_stream_stream(self, request_iterator, context): - """See interfaces.RpcMethodServiceDescription.service_stream_stream.""" - return self._stream_stream(request_iterator, context) - - -def unary_unary_invocation_description( - request_serializer, response_deserializer): - """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. - - Args: - request_serializer: A callable that when called on a request - value returns a bytestring corresponding to that value. - response_deserializer: A callable that when called on a - bytestring returns the response value corresponding to - that bytestring. - - Returns: - An interfaces.RpcMethodInvocationDescription constructed from the given - arguments representing a unary-request/unary-response RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.UNARY_UNARY, None, None, None, None, - request_serializer, None, None, response_deserializer) - - -def unary_stream_invocation_description( - request_serializer, response_deserializer): - """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. - - Args: - request_serializer: A callable that when called on a request - value returns a bytestring corresponding to that value. - response_deserializer: A callable that when called on a - bytestring returns the response value corresponding to - that bytestring. - - Returns: - An interfaces.RpcMethodInvocationDescription constructed from the given - arguments representing a unary-request/streaming-response RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.UNARY_STREAM, None, None, None, None, - request_serializer, None, None, response_deserializer) - - -def stream_unary_invocation_description( - request_serializer, response_deserializer): - """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. - - Args: - request_serializer: A callable that when called on a request - value returns a bytestring corresponding to that value. - response_deserializer: A callable that when called on a - bytestring returns the response value corresponding to - that bytestring. - - Returns: - An interfaces.RpcMethodInvocationDescription constructed from the given - arguments representing a streaming-request/unary-response RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.STREAM_UNARY, None, None, None, None, - request_serializer, None, None, response_deserializer) - - -def stream_stream_invocation_description( - request_serializer, response_deserializer): - """Creates an interfaces.RpcMethodInvocationDescription for an RPC method. - - Args: - request_serializer: A callable that when called on a request - value returns a bytestring corresponding to that value. - response_deserializer: A callable that when called on a - bytestring returns the response value corresponding to - that bytestring. - - Returns: - An interfaces.RpcMethodInvocationDescription constructed from the given - arguments representing a streaming-request/streaming-response RPC - method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.STREAM_STREAM, None, None, None, None, - request_serializer, None, None, response_deserializer) - - -def unary_unary_service_description( - behavior, request_deserializer, response_serializer): - """Creates an interfaces.RpcMethodServiceDescription for the given behavior. - - Args: - behavior: A callable that implements a unary-unary RPC - method that accepts a single request and an interfaces.RpcContext and - returns a single response. - request_deserializer: A callable that when called on a - bytestring returns the request value corresponding to that - bytestring. - response_serializer: A callable that when called on a - response value returns the bytestring corresponding to - that value. - - Returns: - An interfaces.RpcMethodServiceDescription constructed from the given - arguments representing a unary-request/unary-response RPC - method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None, - None, request_deserializer, response_serializer, None) - - -def unary_stream_service_description( - behavior, request_deserializer, response_serializer): - """Creates an interfaces.RpcMethodServiceDescription for the given behavior. - - Args: - behavior: A callable that implements a unary-stream RPC - method that accepts a single request and an interfaces.RpcContext - and returns an iterator of zero or more responses. - request_deserializer: A callable that when called on a - bytestring returns the request value corresponding to that - bytestring. - response_serializer: A callable that when called on a - response value returns the bytestring corresponding to - that value. - - Returns: - An interfaces.RpcMethodServiceDescription constructed from the given - arguments representing a unary-request/streaming-response - RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None, - None, request_deserializer, response_serializer, None) - - -def stream_unary_service_description( - behavior, request_deserializer, response_serializer): - """Creates an interfaces.RpcMethodServiceDescription for the given behavior. - - Args: - behavior: A callable that implements a stream-unary RPC - method that accepts an iterator of zero or more requests - and an interfaces.RpcContext and returns a single response. - request_deserializer: A callable that when called on a - bytestring returns the request value corresponding to that - bytestring. - response_serializer: A callable that when called on a - response value returns the bytestring corresponding to - that value. - - Returns: - An interfaces.RpcMethodServiceDescription constructed from the given - arguments representing a streaming-request/unary-response - RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None, - None, request_deserializer, response_serializer, None) - - -def stream_stream_service_description( - behavior, request_deserializer, response_serializer): - """Creates an interfaces.RpcMethodServiceDescription for the given behavior. - - Args: - behavior: A callable that implements a stream-stream RPC - method that accepts an iterator of zero or more requests - and an interfaces.RpcContext and returns an iterator of - zero or more responses. - request_deserializer: A callable that when called on a - bytestring returns the request value corresponding to that - bytestring. - response_serializer: A callable that when called on a - response value returns the bytestring corresponding to - that value. - - Returns: - An interfaces.RpcMethodServiceDescription constructed from the given - arguments representing a - streaming-request/streaming-response RPC method. - """ - return _RpcMethodDescription( - interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior, - None, request_deserializer, response_serializer, None) diff --git a/src/python/src/grpc/framework/base/__init__.py b/src/python/src/grpc/framework/base/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/base/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/base/_cancellation.py b/src/python/src/grpc/framework/base/_cancellation.py deleted file mode 100644 index ffbc90668f..0000000000 --- a/src/python/src/grpc/framework/base/_cancellation.py +++ /dev/null @@ -1,64 +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. - -"""State and behavior for operation cancellation.""" - -from grpc.framework.base import _interfaces -from grpc.framework.base import interfaces - - -class CancellationManager(_interfaces.CancellationManager): - """An implementation of _interfaces.CancellationManager.""" - - def __init__( - self, lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Constructor. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - ingestion_manager: The _interfaces.IngestionManager for the operation. - expiration_manager: The _interfaces.ExpirationManager for the operation. - """ - self._lock = lock - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - def cancel(self): - """See _interfaces.CancellationManager.cancel for specification.""" - with self._lock: - self._termination_manager.abort(interfaces.Outcome.CANCELLED) - self._transmission_manager.abort(interfaces.Outcome.CANCELLED) - self._ingestion_manager.abort() - self._expiration_manager.abort() diff --git a/src/python/src/grpc/framework/base/_constants.py b/src/python/src/grpc/framework/base/_constants.py deleted file mode 100644 index 8fbdc82782..0000000000 --- a/src/python/src/grpc/framework/base/_constants.py +++ /dev/null @@ -1,32 +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. - -"""Private constants for the package.""" - -INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Base) internal error! :-(' diff --git a/src/python/src/grpc/framework/base/_context.py b/src/python/src/grpc/framework/base/_context.py deleted file mode 100644 index d84871d639..0000000000 --- a/src/python/src/grpc/framework/base/_context.py +++ /dev/null @@ -1,99 +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. - -"""State and behavior for operation context.""" - -import time - -# _interfaces is referenced from specification in this module. -from grpc.framework.base import interfaces -from grpc.framework.base import _interfaces # pylint: disable=unused-import - - -class OperationContext(interfaces.OperationContext): - """An implementation of interfaces.OperationContext.""" - - def __init__( - self, lock, operation_id, local_failure, termination_manager, - transmission_manager): - """Constructor. - - Args: - lock: The operation-wide lock. - operation_id: An object identifying the operation. - local_failure: Whichever one of interfaces.Outcome.SERVICED_FAILURE or - interfaces.Outcome.SERVICER_FAILURE describes local failure of - customer code. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - """ - self._lock = lock - self._local_failure = local_failure - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = None - self._expiration_manager = None - - self.operation_id = operation_id - - def set_ingestion_and_expiration_managers( - self, ingestion_manager, expiration_manager): - """Sets managers with which this OperationContext cooperates. - - Args: - ingestion_manager: The _interfaces.IngestionManager for the operation. - expiration_manager: The _interfaces.ExpirationManager for the operation. - """ - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - def is_active(self): - """See interfaces.OperationContext.is_active for specification.""" - with self._lock: - return self._termination_manager.is_active() - - def add_termination_callback(self, callback): - """See interfaces.OperationContext.add_termination_callback.""" - with self._lock: - self._termination_manager.add_callback(callback) - - def time_remaining(self): - """See interfaces.OperationContext.time_remaining for specification.""" - with self._lock: - deadline = self._expiration_manager.deadline() - return max(0.0, deadline - time.time()) - - def fail(self, exception): - """See interfaces.OperationContext.fail for specification.""" - with self._lock: - self._termination_manager.abort(self._local_failure) - self._transmission_manager.abort(self._local_failure) - self._ingestion_manager.abort() - self._expiration_manager.abort() diff --git a/src/python/src/grpc/framework/base/_emission.py b/src/python/src/grpc/framework/base/_emission.py deleted file mode 100644 index 1829669a72..0000000000 --- a/src/python/src/grpc/framework/base/_emission.py +++ /dev/null @@ -1,125 +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. - -"""State and behavior for handling emitted values.""" - -from grpc.framework.base import interfaces -from grpc.framework.base import _interfaces - - -class _EmissionManager(_interfaces.EmissionManager): - """An implementation of _interfaces.EmissionManager.""" - - def __init__( - self, lock, failure_outcome, termination_manager, transmission_manager): - """Constructor. - - Args: - lock: The operation-wide lock. - failure_outcome: Whichever one of interfaces.Outcome.SERVICED_FAILURE or - interfaces.Outcome.SERVICER_FAILURE describes this object's methods - being called inappropriately by customer code. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - """ - self._lock = lock - self._failure_outcome = failure_outcome - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = None - self._expiration_manager = None - - self._emission_complete = False - - def set_ingestion_manager_and_expiration_manager( - self, ingestion_manager, expiration_manager): - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - def _abort(self): - self._termination_manager.abort(self._failure_outcome) - self._transmission_manager.abort(self._failure_outcome) - self._ingestion_manager.abort() - self._expiration_manager.abort() - - def consume(self, value): - with self._lock: - if self._emission_complete: - self._abort() - else: - self._transmission_manager.inmit(value, False) - - def terminate(self): - with self._lock: - if not self._emission_complete: - self._termination_manager.emission_complete() - self._transmission_manager.inmit(None, True) - self._emission_complete = True - - def consume_and_terminate(self, value): - with self._lock: - if self._emission_complete: - self._abort() - else: - self._termination_manager.emission_complete() - self._transmission_manager.inmit(value, True) - self._emission_complete = True - - -def front_emission_manager(lock, termination_manager, transmission_manager): - """Creates an _interfaces.EmissionManager appropriate for front-side use. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the operation. - - Returns: - An _interfaces.EmissionManager appropriate for front-side use. - """ - return _EmissionManager( - lock, interfaces.Outcome.SERVICED_FAILURE, termination_manager, - transmission_manager) - - -def back_emission_manager(lock, termination_manager, transmission_manager): - """Creates an _interfaces.EmissionManager appropriate for back-side use. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the operation. - - Returns: - An _interfaces.EmissionManager appropriate for back-side use. - """ - return _EmissionManager( - lock, interfaces.Outcome.SERVICER_FAILURE, termination_manager, - transmission_manager) diff --git a/src/python/src/grpc/framework/base/_ends.py b/src/python/src/grpc/framework/base/_ends.py deleted file mode 100644 index 176f3ac06e..0000000000 --- a/src/python/src/grpc/framework/base/_ends.py +++ /dev/null @@ -1,399 +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. - -"""Implementations of FrontLinks and BackLinks.""" - -import collections -import threading -import uuid - -# _interfaces is referenced from specification in this module. -from grpc.framework.base import _cancellation -from grpc.framework.base import _context -from grpc.framework.base import _emission -from grpc.framework.base import _expiration -from grpc.framework.base import _ingestion -from grpc.framework.base import _interfaces # pylint: disable=unused-import -from grpc.framework.base import _reception -from grpc.framework.base import _termination -from grpc.framework.base import _transmission -from grpc.framework.base import interfaces -from grpc.framework.foundation import callable_util - -_IDLE_ACTION_EXCEPTION_LOG_MESSAGE = 'Exception calling idle action!' - - -class _EasyOperation(interfaces.Operation): - """A trivial implementation of interfaces.Operation.""" - - def __init__(self, emission_manager, context, cancellation_manager): - """Constructor. - - Args: - emission_manager: The _interfaces.EmissionManager for the operation that - will accept values emitted by customer code. - context: The interfaces.OperationContext for use by the customer - during the operation. - cancellation_manager: The _interfaces.CancellationManager for the - operation. - """ - self.consumer = emission_manager - self.context = context - self._cancellation_manager = cancellation_manager - - def cancel(self): - self._cancellation_manager.cancel() - - -class _Endlette(object): - """Utility for stateful behavior common to Fronts and Backs.""" - - def __init__(self, pool): - """Constructor. - - Args: - pool: A thread pool to use when calling registered idle actions. - """ - self._lock = threading.Lock() - self._pool = pool - # Dictionary from operation IDs to ReceptionManager-or-None. A None value - # indicates an in-progress fire-and-forget operation for which the customer - # has chosen to ignore results. - self._operations = {} - self._stats = {outcome: 0 for outcome in interfaces.Outcome} - self._idle_actions = [] - - def terminal_action(self, operation_id): - """Constructs the termination action for a single operation. - - Args: - operation_id: An operation ID. - - Returns: - A callable that takes an operation outcome for an argument to be used as - the termination action for the operation associated with the given - operation ID. - """ - def termination_action(outcome): - with self._lock: - self._stats[outcome] += 1 - self._operations.pop(operation_id, None) - if not self._operations: - for action in self._idle_actions: - self._pool.submit(callable_util.with_exceptions_logged( - action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) - self._idle_actions = [] - return termination_action - - def __enter__(self): - self._lock.acquire() - - def __exit__(self, exc_type, exc_val, exc_tb): - self._lock.release() - - def get_operation(self, operation_id): - return self._operations.get(operation_id, None) - - def add_operation(self, operation_id, operation_reception_manager): - self._operations[operation_id] = operation_reception_manager - - def operation_stats(self): - with self._lock: - return dict(self._stats) - - def add_idle_action(self, action): - with self._lock: - if self._operations: - self._idle_actions.append(action) - else: - self._pool.submit(callable_util.with_exceptions_logged( - action, _IDLE_ACTION_EXCEPTION_LOG_MESSAGE)) - - -class _FrontManagement( - collections.namedtuple( - '_FrontManagement', - ('reception', 'emission', 'operation', 'cancellation'))): - """Just a trivial helper class to bundle four fellow-traveling objects.""" - - -def _front_operate( - callback, work_pool, transmission_pool, utility_pool, - termination_action, operation_id, name, payload, complete, timeout, - subscription, trace_id): - """Constructs objects necessary for front-side operation management. - - Args: - callback: A callable that accepts interfaces.FrontToBackTickets and - delivers them to the other side of the operation. Execution of this - callable may take any arbitrary length of time. - work_pool: A thread pool in which to execute customer code. - transmission_pool: A thread pool to use for transmitting to the other side - of the operation. - utility_pool: A thread pool for utility tasks. - termination_action: A no-arg behavior to be called upon operation - completion. - operation_id: An object identifying the operation. - name: The name of the method being called during the operation. - payload: The first customer-significant value to be transmitted to the other - side. May be None if there is no such value or if the customer chose not - to pass it at operation invocation. - complete: A boolean indicating whether or not additional payloads will be - supplied by the customer. - timeout: A length of time in seconds to allow for the operation. - subscription: A interfaces.ServicedSubscription describing the - customer's interest in the results of the operation. - trace_id: A uuid.UUID identifying a set of related operations to which this - operation belongs. May be None. - - Returns: - A _FrontManagement object bundling together the - _interfaces.ReceptionManager, _interfaces.EmissionManager, - _context.OperationContext, and _interfaces.CancellationManager for the - operation. - """ - lock = threading.Lock() - with lock: - termination_manager = _termination.front_termination_manager( - work_pool, utility_pool, termination_action, subscription.kind) - transmission_manager = _transmission.front_transmission_manager( - lock, transmission_pool, callback, operation_id, name, - subscription.kind, trace_id, timeout, termination_manager) - operation_context = _context.OperationContext( - lock, operation_id, interfaces.Outcome.SERVICED_FAILURE, - termination_manager, transmission_manager) - emission_manager = _emission.front_emission_manager( - lock, termination_manager, transmission_manager) - ingestion_manager = _ingestion.front_ingestion_manager( - lock, work_pool, subscription, termination_manager, - transmission_manager, operation_context) - expiration_manager = _expiration.front_expiration_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - timeout) - reception_manager = _reception.front_reception_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager) - cancellation_manager = _cancellation.CancellationManager( - lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager) - - termination_manager.set_expiration_manager(expiration_manager) - transmission_manager.set_ingestion_and_expiration_managers( - ingestion_manager, expiration_manager) - operation_context.set_ingestion_and_expiration_managers( - ingestion_manager, expiration_manager) - emission_manager.set_ingestion_manager_and_expiration_manager( - ingestion_manager, expiration_manager) - ingestion_manager.set_expiration_manager(expiration_manager) - - transmission_manager.inmit(payload, complete) - - if subscription.kind is interfaces.ServicedSubscription.Kind.NONE: - returned_reception_manager = None - else: - returned_reception_manager = reception_manager - - return _FrontManagement( - returned_reception_manager, emission_manager, operation_context, - cancellation_manager) - - -class FrontLink(interfaces.FrontLink): - """An implementation of interfaces.FrontLink.""" - - def __init__(self, work_pool, transmission_pool, utility_pool): - """Constructor. - - Args: - work_pool: A thread pool to be used for executing customer code. - transmission_pool: A thread pool to be used for transmitting values to - the other side of the operation. - utility_pool: A thread pool to be used for utility tasks. - """ - self._endlette = _Endlette(utility_pool) - self._work_pool = work_pool - self._transmission_pool = transmission_pool - self._utility_pool = utility_pool - self._callback = None - - self._operations = {} - - def join_rear_link(self, rear_link): - """See interfaces.ForeLink.join_rear_link for specification.""" - with self._endlette: - self._callback = rear_link.accept_front_to_back_ticket - - def operation_stats(self): - """See interfaces.End.operation_stats for specification.""" - return self._endlette.operation_stats() - - def add_idle_action(self, action): - """See interfaces.End.add_idle_action for specification.""" - self._endlette.add_idle_action(action) - - def operate( - self, name, payload, complete, timeout, subscription, trace_id): - """See interfaces.Front.operate for specification.""" - operation_id = uuid.uuid4() - with self._endlette: - management = _front_operate( - self._callback, self._work_pool, self._transmission_pool, - self._utility_pool, self._endlette.terminal_action(operation_id), - operation_id, name, payload, complete, timeout, subscription, - trace_id) - self._endlette.add_operation(operation_id, management.reception) - return _EasyOperation( - management.emission, management.operation, management.cancellation) - - def accept_back_to_front_ticket(self, ticket): - """See interfaces.End.act for specification.""" - with self._endlette: - reception_manager = self._endlette.get_operation(ticket.operation_id) - if reception_manager: - reception_manager.receive_ticket(ticket) - - -def _back_operate( - servicer, callback, work_pool, transmission_pool, utility_pool, - termination_action, ticket, default_timeout, maximum_timeout): - """Constructs objects necessary for back-side operation management. - - Also begins back-side operation by feeding the first received ticket into the - constructed _interfaces.ReceptionManager. - - Args: - servicer: An interfaces.Servicer for servicing operations. - callback: A callable that accepts interfaces.BackToFrontTickets and - delivers them to the other side of the operation. Execution of this - callable may take any arbitrary length of time. - work_pool: A thread pool in which to execute customer code. - transmission_pool: A thread pool to use for transmitting to the other side - of the operation. - utility_pool: A thread pool for utility tasks. - termination_action: A no-arg behavior to be called upon operation - completion. - ticket: The first interfaces.FrontToBackTicket received for the operation. - default_timeout: A length of time in seconds to be used as the default - time alloted for a single operation. - maximum_timeout: A length of time in seconds to be used as the maximum - time alloted for a single operation. - - Returns: - The _interfaces.ReceptionManager to be used for the operation. - """ - lock = threading.Lock() - with lock: - termination_manager = _termination.back_termination_manager( - work_pool, utility_pool, termination_action, ticket.subscription) - transmission_manager = _transmission.back_transmission_manager( - lock, transmission_pool, callback, ticket.operation_id, - termination_manager, ticket.subscription) - operation_context = _context.OperationContext( - lock, ticket.operation_id, interfaces.Outcome.SERVICER_FAILURE, - termination_manager, transmission_manager) - emission_manager = _emission.back_emission_manager( - lock, termination_manager, transmission_manager) - ingestion_manager = _ingestion.back_ingestion_manager( - lock, work_pool, servicer, termination_manager, - transmission_manager, operation_context, emission_manager) - expiration_manager = _expiration.back_expiration_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - ticket.timeout, default_timeout, maximum_timeout) - reception_manager = _reception.back_reception_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager) - - termination_manager.set_expiration_manager(expiration_manager) - transmission_manager.set_ingestion_and_expiration_managers( - ingestion_manager, expiration_manager) - operation_context.set_ingestion_and_expiration_managers( - ingestion_manager, expiration_manager) - emission_manager.set_ingestion_manager_and_expiration_manager( - ingestion_manager, expiration_manager) - ingestion_manager.set_expiration_manager(expiration_manager) - - reception_manager.receive_ticket(ticket) - - return reception_manager - - -class BackLink(interfaces.BackLink): - """An implementation of interfaces.BackLink.""" - - def __init__( - self, servicer, work_pool, transmission_pool, utility_pool, - default_timeout, maximum_timeout): - """Constructor. - - Args: - servicer: An interfaces.Servicer for servicing operations. - work_pool: A thread pool in which to execute customer code. - transmission_pool: A thread pool to use for transmitting to the other side - of the operation. - utility_pool: A thread pool for utility tasks. - default_timeout: A length of time in seconds to be used as the default - time alloted for a single operation. - maximum_timeout: A length of time in seconds to be used as the maximum - time alloted for a single operation. - """ - self._endlette = _Endlette(utility_pool) - self._servicer = servicer - self._work_pool = work_pool - self._transmission_pool = transmission_pool - self._utility_pool = utility_pool - self._default_timeout = default_timeout - self._maximum_timeout = maximum_timeout - self._callback = None - - def join_fore_link(self, fore_link): - """See interfaces.RearLink.join_fore_link for specification.""" - with self._endlette: - self._callback = fore_link.accept_back_to_front_ticket - - def accept_front_to_back_ticket(self, ticket): - """See interfaces.RearLink.accept_front_to_back_ticket for specification.""" - with self._endlette: - reception_manager = self._endlette.get_operation(ticket.operation_id) - if reception_manager is None: - reception_manager = _back_operate( - self._servicer, self._callback, self._work_pool, - self._transmission_pool, self._utility_pool, - self._endlette.terminal_action(ticket.operation_id), ticket, - self._default_timeout, self._maximum_timeout) - self._endlette.add_operation(ticket.operation_id, reception_manager) - else: - reception_manager.receive_ticket(ticket) - - def operation_stats(self): - """See interfaces.End.operation_stats for specification.""" - return self._endlette.operation_stats() - - def add_idle_action(self, action): - """See interfaces.End.add_idle_action for specification.""" - self._endlette.add_idle_action(action) diff --git a/src/python/src/grpc/framework/base/_expiration.py b/src/python/src/grpc/framework/base/_expiration.py deleted file mode 100644 index 17acbef4c1..0000000000 --- a/src/python/src/grpc/framework/base/_expiration.py +++ /dev/null @@ -1,158 +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. - -"""State and behavior for operation expiration.""" - -import time - -from grpc.framework.base import _interfaces -from grpc.framework.base import interfaces -from grpc.framework.foundation import later - - -class _ExpirationManager(_interfaces.ExpirationManager): - """An implementation of _interfaces.ExpirationManager.""" - - def __init__( - self, lock, termination_manager, transmission_manager, ingestion_manager, - commencement, timeout, maximum_timeout): - """Constructor. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - ingestion_manager: The _interfaces.IngestionManager for the operation. - commencement: The time in seconds since the epoch at which the operation - began. - timeout: A length of time in seconds to allow for the operation to run. - maximum_timeout: The maximum length of time in seconds to allow for the - operation to run despite what is requested via this object's - change_timout method. - """ - self._lock = lock - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = ingestion_manager - self._commencement = commencement - self._maximum_timeout = maximum_timeout - - self._timeout = timeout - self._deadline = commencement + timeout - self._index = None - self._future = None - - def _expire(self, index): - with self._lock: - if self._future is not None and index == self._index: - self._future = None - self._termination_manager.abort(interfaces.Outcome.EXPIRED) - self._transmission_manager.abort(interfaces.Outcome.EXPIRED) - self._ingestion_manager.abort() - - def start(self): - self._index = 0 - self._future = later.later(self._timeout, lambda: self._expire(0)) - - def change_timeout(self, timeout): - if self._future is not None and timeout != self._timeout: - self._future.cancel() - new_timeout = min(timeout, self._maximum_timeout) - new_index = self._index + 1 - self._timeout = new_timeout - self._deadline = self._commencement + new_timeout - self._index = new_index - delay = self._deadline - time.time() - self._future = later.later( - delay, lambda: self._expire(new_index)) - - def deadline(self): - return self._deadline - - def abort(self): - if self._future: - self._future.cancel() - self._future = None - self._deadline_index = None - - -def front_expiration_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - timeout): - """Creates an _interfaces.ExpirationManager appropriate for front-side use. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - ingestion_manager: The _interfaces.IngestionManager for the operation. - timeout: A length of time in seconds to allow for the operation to run. - - Returns: - An _interfaces.ExpirationManager appropriate for front-side use. - """ - commencement = time.time() - expiration_manager = _ExpirationManager( - lock, termination_manager, transmission_manager, ingestion_manager, - commencement, timeout, timeout) - expiration_manager.start() - return expiration_manager - - -def back_expiration_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - timeout, default_timeout, maximum_timeout): - """Creates an _interfaces.ExpirationManager appropriate for back-side use. - - Args: - lock: The operation-wide lock. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - ingestion_manager: The _interfaces.IngestionManager for the operation. - timeout: A length of time in seconds to allow for the operation to run. May - be None in which case default_timeout will be used. - default_timeout: The default length of time in seconds to allow for the - operation to run if the front-side customer has not specified such a value - (or if the value they specified is not yet known). - maximum_timeout: The maximum length of time in seconds to allow for the - operation to run. - - Returns: - An _interfaces.ExpirationManager appropriate for back-side use. - """ - commencement = time.time() - expiration_manager = _ExpirationManager( - lock, termination_manager, transmission_manager, ingestion_manager, - commencement, default_timeout if timeout is None else timeout, - maximum_timeout) - expiration_manager.start() - return expiration_manager diff --git a/src/python/src/grpc/framework/base/_ingestion.py b/src/python/src/grpc/framework/base/_ingestion.py deleted file mode 100644 index 06d5b92f0b..0000000000 --- a/src/python/src/grpc/framework/base/_ingestion.py +++ /dev/null @@ -1,442 +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. - -"""State and behavior for ingestion during an operation.""" - -import abc -import collections - -from grpc.framework.base import _constants -from grpc.framework.base import _interfaces -from grpc.framework.base import exceptions -from grpc.framework.base import interfaces -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import callable_util -from grpc.framework.foundation import stream - -_CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE = 'Exception initializing ingestion!' -_CONSUME_EXCEPTION_LOG_MESSAGE = 'Exception during ingestion!' - - -class _ConsumerCreation(collections.namedtuple( - '_ConsumerCreation', ('consumer', 'remote_error', 'abandoned'))): - """A sum type for the outcome of ingestion initialization. - - Either consumer will be non-None, remote_error will be True, or abandoned will - be True. - - Attributes: - consumer: A stream.Consumer for ingesting payloads. - remote_error: A boolean indicating that the consumer could not be created - due to an error on the remote side of the operation. - abandoned: A boolean indicating that the consumer creation was abandoned. - """ - - -class _EmptyConsumer(stream.Consumer): - """A no-operative stream.Consumer that ignores all inputs and calls.""" - - def consume(self, value): - """See stream.Consumer.consume for specification.""" - - def terminate(self): - """See stream.Consumer.terminate for specification.""" - - def consume_and_terminate(self, value): - """See stream.Consumer.consume_and_terminate for specification.""" - - -class _ConsumerCreator(object): - """Common specification of different consumer-creating behavior.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def create_consumer(self, requirement): - """Creates the stream.Consumer to which customer payloads will be delivered. - - Any exceptions raised by this method should be attributed to and treated as - defects in the serviced or servicer code called by this method. - - Args: - requirement: A value required by this _ConsumerCreator for consumer - creation. - - Returns: - A _ConsumerCreation describing the result of consumer creation. - """ - raise NotImplementedError() - - -class _FrontConsumerCreator(_ConsumerCreator): - """A _ConsumerCreator appropriate for front-side use.""" - - def __init__(self, subscription, operation_context): - """Constructor. - - Args: - subscription: The serviced's interfaces.ServicedSubscription for the - operation. - operation_context: The interfaces.OperationContext object for the - operation. - """ - self._subscription = subscription - self._operation_context = operation_context - - def create_consumer(self, requirement): - """See _ConsumerCreator.create_consumer for specification.""" - if self._subscription.kind is interfaces.ServicedSubscription.Kind.FULL: - try: - return _ConsumerCreation( - self._subscription.ingestor.consumer(self._operation_context), - False, False) - except abandonment.Abandoned: - return _ConsumerCreation(None, False, True) - else: - return _ConsumerCreation(_EmptyConsumer(), False, False) - - -class _BackConsumerCreator(_ConsumerCreator): - """A _ConsumerCreator appropriate for back-side use.""" - - def __init__(self, servicer, operation_context, emission_consumer): - """Constructor. - - Args: - servicer: The interfaces.Servicer that will service the operation. - operation_context: The interfaces.OperationContext object for the - operation. - emission_consumer: The stream.Consumer object to which payloads emitted - from the operation will be passed. - """ - self._servicer = servicer - self._operation_context = operation_context - self._emission_consumer = emission_consumer - - def create_consumer(self, requirement): - """See _ConsumerCreator.create_consumer for full specification. - - Args: - requirement: The name of the Servicer method to be called during this - operation. - - Returns: - A _ConsumerCreation describing the result of consumer creation. - """ - try: - return _ConsumerCreation( - self._servicer.service( - requirement, self._operation_context, self._emission_consumer), - False, False) - except exceptions.NoSuchMethodError: - return _ConsumerCreation(None, True, False) - except abandonment.Abandoned: - return _ConsumerCreation(None, False, True) - - -class _WrappedConsumer(object): - """Wraps a consumer to catch the exceptions that it is allowed to throw.""" - - def __init__(self, consumer): - """Constructor. - - Args: - consumer: A stream.Consumer that may raise abandonment.Abandoned from any - of its methods. - """ - self._consumer = consumer - - def moar(self, payload, complete): - """Makes progress with the wrapped consumer. - - This method catches all exceptions allowed to be thrown by the wrapped - consumer. Any exceptions raised by this method should be blamed on the - customer-supplied consumer. - - Args: - payload: A customer-significant payload object. May be None only if - complete is True. - complete: Whether or not the end of the payload sequence has been reached. - Must be True if payload is None. - - Returns: - True if the wrapped consumer made progress or False if the wrapped - consumer raised abandonment.Abandoned to indicate its abandonment of - progress. - """ - try: - if payload is None: - self._consumer.terminate() - elif complete: - self._consumer.consume_and_terminate(payload) - else: - self._consumer.consume(payload) - return True - except abandonment.Abandoned: - return False - - -class _IngestionManager(_interfaces.IngestionManager): - """An implementation of _interfaces.IngestionManager.""" - - def __init__( - self, lock, pool, consumer_creator, failure_outcome, termination_manager, - transmission_manager): - """Constructor. - - Args: - lock: The operation-wide lock. - pool: A thread pool in which to execute customer code. - consumer_creator: A _ConsumerCreator wrapping the portion of customer code - that when called returns the stream.Consumer with which the customer - code will ingest payload values. - failure_outcome: Whichever one of - interfaces.Outcome.SERVICED_FAILURE or - interfaces.Outcome.SERVICER_FAILURE describes local failure of - customer code. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - """ - self._lock = lock - self._pool = pool - self._consumer_creator = consumer_creator - self._failure_outcome = failure_outcome - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._expiration_manager = None - - self._wrapped_ingestion_consumer = None - self._pending_ingestion = [] - self._ingestion_complete = False - self._processing = False - - def set_expiration_manager(self, expiration_manager): - self._expiration_manager = expiration_manager - - def _abort_internal_only(self): - self._wrapped_ingestion_consumer = None - self._pending_ingestion = None - - def _abort_and_notify(self, outcome): - self._abort_internal_only() - self._termination_manager.abort(outcome) - self._transmission_manager.abort(outcome) - self._expiration_manager.abort() - - def _next(self): - """Computes the next step for ingestion. - - Returns: - A payload, complete, continue triplet indicating what payload (if any) is - available to feed into customer code, whether or not the sequence of - payloads has terminated, and whether or not there is anything - immediately actionable to call customer code to do. - """ - if self._pending_ingestion is None: - return None, False, False - elif self._pending_ingestion: - payload = self._pending_ingestion.pop(0) - complete = self._ingestion_complete and not self._pending_ingestion - return payload, complete, True - elif self._ingestion_complete: - return None, True, True - else: - return None, False, False - - def _process(self, wrapped_ingestion_consumer, payload, complete): - """A method to call to execute customer code. - - This object's lock must *not* be held when calling this method. - - Args: - wrapped_ingestion_consumer: The _WrappedConsumer with which to pass - payloads to customer code. - payload: A customer payload. May be None only if complete is True. - complete: Whether or not the sequence of payloads to pass to the customer - has concluded. - """ - while True: - consumption_outcome = callable_util.call_logging_exceptions( - wrapped_ingestion_consumer.moar, _CONSUME_EXCEPTION_LOG_MESSAGE, - payload, complete) - if consumption_outcome.exception is None: - if consumption_outcome.return_value: - with self._lock: - if complete: - self._pending_ingestion = None - self._termination_manager.ingestion_complete() - return - else: - payload, complete, moar = self._next() - if not moar: - self._processing = False - return - else: - with self._lock: - if self._pending_ingestion is not None: - self._abort_and_notify(self._failure_outcome) - self._processing = False - return - else: - with self._lock: - self._abort_and_notify(self._failure_outcome) - self._processing = False - return - - def start(self, requirement): - if self._pending_ingestion is not None: - def initialize(): - consumer_creation_outcome = callable_util.call_logging_exceptions( - self._consumer_creator.create_consumer, - _CREATE_CONSUMER_EXCEPTION_LOG_MESSAGE, requirement) - if consumer_creation_outcome.return_value is None: - with self._lock: - self._abort_and_notify(self._failure_outcome) - self._processing = False - elif consumer_creation_outcome.return_value.remote_error: - with self._lock: - self._abort_and_notify(interfaces.Outcome.RECEPTION_FAILURE) - self._processing = False - elif consumer_creation_outcome.return_value.abandoned: - with self._lock: - if self._pending_ingestion is not None: - self._abort_and_notify(self._failure_outcome) - self._processing = False - else: - wrapped_ingestion_consumer = _WrappedConsumer( - consumer_creation_outcome.return_value.consumer) - with self._lock: - self._wrapped_ingestion_consumer = wrapped_ingestion_consumer - payload, complete, moar = self._next() - if not moar: - self._processing = False - return - - self._process(wrapped_ingestion_consumer, payload, complete) - - self._pool.submit( - callable_util.with_exceptions_logged( - initialize, _constants.INTERNAL_ERROR_LOG_MESSAGE)) - self._processing = True - - def consume(self, payload): - if self._ingestion_complete: - self._abort_and_notify(self._failure_outcome) - elif self._pending_ingestion is not None: - if self._processing: - self._pending_ingestion.append(payload) - else: - self._pool.submit( - callable_util.with_exceptions_logged( - self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), - self._wrapped_ingestion_consumer, payload, False) - self._processing = True - - def terminate(self): - if self._ingestion_complete: - self._abort_and_notify(self._failure_outcome) - else: - self._ingestion_complete = True - if self._pending_ingestion is not None and not self._processing: - self._pool.submit( - callable_util.with_exceptions_logged( - self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), - self._wrapped_ingestion_consumer, None, True) - self._processing = True - - def consume_and_terminate(self, payload): - if self._ingestion_complete: - self._abort_and_notify(self._failure_outcome) - else: - self._ingestion_complete = True - if self._pending_ingestion is not None: - if self._processing: - self._pending_ingestion.append(payload) - else: - self._pool.submit( - callable_util.with_exceptions_logged( - self._process, _constants.INTERNAL_ERROR_LOG_MESSAGE), - self._wrapped_ingestion_consumer, payload, True) - self._processing = True - - def abort(self): - """See _interfaces.IngestionManager.abort for specification.""" - self._abort_internal_only() - - -def front_ingestion_manager( - lock, pool, subscription, termination_manager, transmission_manager, - operation_context): - """Creates an IngestionManager appropriate for front-side use. - - Args: - lock: The operation-wide lock. - pool: A thread pool in which to execute customer code. - subscription: A interfaces.ServicedSubscription indicating the - customer's interest in the results of the operation. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - operation_context: A interfaces.OperationContext for the operation. - - Returns: - An IngestionManager appropriate for front-side use. - """ - ingestion_manager = _IngestionManager( - lock, pool, _FrontConsumerCreator(subscription, operation_context), - interfaces.Outcome.SERVICED_FAILURE, termination_manager, - transmission_manager) - ingestion_manager.start(None) - return ingestion_manager - - -def back_ingestion_manager( - lock, pool, servicer, termination_manager, transmission_manager, - operation_context, emission_consumer): - """Creates an IngestionManager appropriate for back-side use. - - Args: - lock: The operation-wide lock. - pool: A thread pool in which to execute customer code. - servicer: A interfaces.Servicer for servicing the operation. - termination_manager: The _interfaces.TerminationManager for the operation. - transmission_manager: The _interfaces.TransmissionManager for the - operation. - operation_context: A interfaces.OperationContext for the operation. - emission_consumer: The _interfaces.EmissionConsumer for the operation. - - Returns: - An IngestionManager appropriate for back-side use. - """ - ingestion_manager = _IngestionManager( - lock, pool, _BackConsumerCreator( - servicer, operation_context, emission_consumer), - interfaces.Outcome.SERVICER_FAILURE, termination_manager, - transmission_manager) - return ingestion_manager diff --git a/src/python/src/grpc/framework/base/_interfaces.py b/src/python/src/grpc/framework/base/_interfaces.py deleted file mode 100644 index d88cf76590..0000000000 --- a/src/python/src/grpc/framework/base/_interfaces.py +++ /dev/null @@ -1,271 +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. - -"""Package-internal interfaces.""" - -import abc - -# interfaces is referenced from specification in this module. -from grpc.framework.base import interfaces # pylint: disable=unused-import -from grpc.framework.foundation import stream - - -class TerminationManager(object): - """An object responsible for handling the termination of an operation.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_expiration_manager(self, expiration_manager): - """Sets the ExpirationManager with which this object will cooperate.""" - raise NotImplementedError() - - @abc.abstractmethod - def is_active(self): - """Reports whether or not the operation is active. - - Returns: - True if the operation is active or False if the operation has terminated. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_callback(self, callback): - """Registers a callback to be called on operation termination. - - If the operation has already terminated, the callback will be called - immediately. - - Args: - callback: A callable that will be passed an interfaces.Outcome value. - """ - raise NotImplementedError() - - @abc.abstractmethod - def emission_complete(self): - """Indicates that emissions from customer code have completed.""" - raise NotImplementedError() - - @abc.abstractmethod - def transmission_complete(self): - """Indicates that transmissions to the remote end are complete.""" - raise NotImplementedError() - - @abc.abstractmethod - def ingestion_complete(self): - """Indicates that customer code ingestion of received values is complete.""" - raise NotImplementedError() - - @abc.abstractmethod - def abort(self, outcome): - """Indicates that the operation must abort for the indicated reason. - - Args: - outcome: An interfaces.Outcome indicating operation abortion. - """ - raise NotImplementedError() - - -class TransmissionManager(object): - """A manager responsible for transmitting to the other end of an operation.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def inmit(self, emission, complete): - """Accepts a value for transmission to the other end of the operation. - - Args: - emission: A value of some significance to the customer to be transmitted - to the other end of the operation. May be None only if complete is True. - complete: A boolean that if True indicates that customer code has emitted - all values it intends to emit. - """ - raise NotImplementedError() - - @abc.abstractmethod - def abort(self, outcome): - """Indicates that the operation has aborted for the indicated reason. - - Args: - outcome: An interfaces.Outcome indicating operation abortion. - """ - raise NotImplementedError() - - -class EmissionManager(stream.Consumer): - """A manager of values emitted by customer code.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_ingestion_manager_and_expiration_manager( - self, ingestion_manager, expiration_manager): - """Sets two other objects with which this EmissionManager will cooperate. - - Args: - ingestion_manager: The IngestionManager for the operation. - expiration_manager: The ExpirationManager for the operation. - """ - raise NotImplementedError() - - @abc.abstractmethod - def consume(self, value): - """Accepts a value emitted by customer code. - - This method should only be called by customer code. - - Args: - value: Any value of significance to the customer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def terminate(self): - """Indicates that no more values will be emitted by customer code. - - This method should only be called by customer code. - - Implementations of this method may be idempotent and forgive customer code - calling this method more than once. - """ - raise NotImplementedError() - - @abc.abstractmethod - def consume_and_terminate(self, value): - """Accepts the last value emitted by customer code. - - This method should only be called by customer code. - - Args: - value: Any value of significance to the customer. - """ - raise NotImplementedError() - - -class IngestionManager(stream.Consumer): - """A manager responsible for executing customer code.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_expiration_manager(self, expiration_manager): - """Sets the ExpirationManager with which this object will cooperate.""" - raise NotImplementedError() - - @abc.abstractmethod - def start(self, requirement): - """Commences execution of customer code. - - Args: - requirement: Some value unavailable at the time of this object's - construction that is required to begin executing customer code. - """ - raise NotImplementedError() - - @abc.abstractmethod - def consume(self, payload): - """Accepts a customer-significant value to be supplied to customer code. - - Args: - payload: Some customer-significant value. - """ - raise NotImplementedError() - - @abc.abstractmethod - def terminate(self): - """Indicates the end of values to be supplied to customer code.""" - raise NotImplementedError() - - @abc.abstractmethod - def consume_and_terminate(self, payload): - """Accepts the last value to be supplied to customer code. - - Args: - payload: Some customer-significant value (and the last such value). - """ - raise NotImplementedError() - - @abc.abstractmethod - def abort(self): - """Indicates to this manager that the operation has aborted.""" - raise NotImplementedError() - - -class ExpirationManager(object): - """A manager responsible for aborting the operation if it runs out of time.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def change_timeout(self, timeout): - """Changes the timeout allotted for the operation. - - Operation duration is always measure from the beginning of the operation; - calling this method changes the operation's allotted time to timeout total - seconds, not timeout seconds from the time of this method call. - - Args: - timeout: A length of time in seconds to allow for the operation. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deadline(self): - """Returns the time until which the operation is allowed to run. - - Returns: - The time (seconds since the epoch) at which the operation will expire. - """ - raise NotImplementedError() - - @abc.abstractmethod - def abort(self): - """Indicates to this manager that the operation has aborted.""" - raise NotImplementedError() - - -class ReceptionManager(object): - """A manager responsible for receiving tickets from the other end.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def receive_ticket(self, ticket): - """Handle a ticket from the other side of the operation. - - Args: - ticket: An interfaces.BackToFrontTicket or interfaces.FrontToBackTicket - appropriate to this end of the operation and this object. - """ - raise NotImplementedError() - - -class CancellationManager(object): - """A manager of operation cancellation.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def cancel(self): - """Cancels the operation.""" - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/base/_reception.py b/src/python/src/grpc/framework/base/_reception.py deleted file mode 100644 index dd428964f1..0000000000 --- a/src/python/src/grpc/framework/base/_reception.py +++ /dev/null @@ -1,399 +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. - -"""State and behavior for ticket reception.""" - -import abc - -from grpc.framework.base import interfaces -from grpc.framework.base import _interfaces - -_INITIAL_FRONT_TO_BACK_TICKET_KINDS = ( - interfaces.FrontToBackTicket.Kind.COMMENCEMENT, - interfaces.FrontToBackTicket.Kind.ENTIRE, -) - - -class _Receiver(object): - """Common specification of different ticket-handling behavior.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def abort_if_abortive(self, ticket): - """Aborts the operation if the ticket is abortive. - - Args: - ticket: A just-arrived ticket. - - Returns: - A boolean indicating whether or not this Receiver aborted the operation - based on the ticket. - """ - raise NotImplementedError() - - @abc.abstractmethod - def receive(self, ticket): - """Handles a just-arrived ticket. - - Args: - ticket: A just-arrived ticket. - - Returns: - A boolean indicating whether or not the ticket was terminal (i.e. whether - or not non-abortive tickets are legal after this one). - """ - raise NotImplementedError() - - @abc.abstractmethod - def reception_failure(self): - """Aborts the operation with an indication of reception failure.""" - raise NotImplementedError() - - -def _abort( - outcome, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Indicates abortion with the given outcome to the given managers.""" - termination_manager.abort(outcome) - transmission_manager.abort(outcome) - ingestion_manager.abort() - expiration_manager.abort() - - -def _abort_if_abortive( - ticket, abortive, termination_manager, transmission_manager, - ingestion_manager, expiration_manager): - """Determines a ticket's being abortive and if so aborts the operation. - - Args: - ticket: A just-arrived ticket. - abortive: A callable that takes a ticket and returns an interfaces.Outcome - indicating that the operation should be aborted or None indicating that - the operation should not be aborted. - termination_manager: The operation's _interfaces.TerminationManager. - transmission_manager: The operation's _interfaces.TransmissionManager. - ingestion_manager: The operation's _interfaces.IngestionManager. - expiration_manager: The operation's _interfaces.ExpirationManager. - - Returns: - True if the operation was aborted; False otherwise. - """ - abortion_outcome = abortive(ticket) - if abortion_outcome is None: - return False - else: - _abort( - abortion_outcome, termination_manager, transmission_manager, - ingestion_manager, expiration_manager) - return True - - -def _reception_failure( - termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Aborts the operation with an indication of reception failure.""" - _abort( - interfaces.Outcome.RECEPTION_FAILURE, termination_manager, - transmission_manager, ingestion_manager, expiration_manager) - - -class _BackReceiver(_Receiver): - """Ticket-handling specific to the back side of an operation.""" - - def __init__( - self, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Constructor. - - Args: - termination_manager: The operation's _interfaces.TerminationManager. - transmission_manager: The operation's _interfaces.TransmissionManager. - ingestion_manager: The operation's _interfaces.IngestionManager. - expiration_manager: The operation's _interfaces.ExpirationManager. - """ - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - self._first_ticket_seen = False - self._last_ticket_seen = False - - def _abortive(self, ticket): - """Determines whether or not (and if so, how) a ticket is abortive. - - Args: - ticket: A just-arrived ticket. - - Returns: - An interfaces.Outcome value describing operation abortion if the - ticket is abortive or None if the ticket is not abortive. - """ - if ticket.kind is interfaces.FrontToBackTicket.Kind.CANCELLATION: - return interfaces.Outcome.CANCELLED - elif ticket.kind is interfaces.FrontToBackTicket.Kind.EXPIRATION: - return interfaces.Outcome.EXPIRED - elif ticket.kind is interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE: - return interfaces.Outcome.SERVICED_FAILURE - elif ticket.kind is interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE: - return interfaces.Outcome.SERVICED_FAILURE - elif (ticket.kind in _INITIAL_FRONT_TO_BACK_TICKET_KINDS and - self._first_ticket_seen): - return interfaces.Outcome.RECEPTION_FAILURE - elif self._last_ticket_seen: - return interfaces.Outcome.RECEPTION_FAILURE - else: - return None - - def abort_if_abortive(self, ticket): - """See _Receiver.abort_if_abortive for specification.""" - return _abort_if_abortive( - ticket, self._abortive, self._termination_manager, - self._transmission_manager, self._ingestion_manager, - self._expiration_manager) - - def receive(self, ticket): - """See _Receiver.receive for specification.""" - if ticket.timeout is not None: - self._expiration_manager.change_timeout(ticket.timeout) - - if ticket.kind is interfaces.FrontToBackTicket.Kind.COMMENCEMENT: - self._first_ticket_seen = True - self._ingestion_manager.start(ticket.name) - if ticket.payload is not None: - self._ingestion_manager.consume(ticket.payload) - elif ticket.kind is interfaces.FrontToBackTicket.Kind.CONTINUATION: - self._ingestion_manager.consume(ticket.payload) - elif ticket.kind is interfaces.FrontToBackTicket.Kind.COMPLETION: - self._last_ticket_seen = True - if ticket.payload is None: - self._ingestion_manager.terminate() - else: - self._ingestion_manager.consume_and_terminate(ticket.payload) - else: - self._first_ticket_seen = True - self._last_ticket_seen = True - self._ingestion_manager.start(ticket.name) - if ticket.payload is None: - self._ingestion_manager.terminate() - else: - self._ingestion_manager.consume_and_terminate(ticket.payload) - - def reception_failure(self): - """See _Receiver.reception_failure for specification.""" - _reception_failure( - self._termination_manager, self._transmission_manager, - self._ingestion_manager, self._expiration_manager) - - -class _FrontReceiver(_Receiver): - """Ticket-handling specific to the front side of an operation.""" - - def __init__( - self, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Constructor. - - Args: - termination_manager: The operation's _interfaces.TerminationManager. - transmission_manager: The operation's _interfaces.TransmissionManager. - ingestion_manager: The operation's _interfaces.IngestionManager. - expiration_manager: The operation's _interfaces.ExpirationManager. - """ - self._termination_manager = termination_manager - self._transmission_manager = transmission_manager - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - self._last_ticket_seen = False - - def _abortive(self, ticket): - """Determines whether or not (and if so, how) a ticket is abortive. - - Args: - ticket: A just-arrived ticket. - - Returns: - An interfaces.Outcome value describing operation abortion if the ticket - is abortive or None if the ticket is not abortive. - """ - if ticket.kind is interfaces.BackToFrontTicket.Kind.CANCELLATION: - return interfaces.Outcome.CANCELLED - elif ticket.kind is interfaces.BackToFrontTicket.Kind.EXPIRATION: - return interfaces.Outcome.EXPIRED - elif ticket.kind is interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE: - return interfaces.Outcome.SERVICER_FAILURE - elif ticket.kind is interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE: - return interfaces.Outcome.SERVICER_FAILURE - elif self._last_ticket_seen: - return interfaces.Outcome.RECEPTION_FAILURE - else: - return None - - def abort_if_abortive(self, ticket): - """See _Receiver.abort_if_abortive for specification.""" - return _abort_if_abortive( - ticket, self._abortive, self._termination_manager, - self._transmission_manager, self._ingestion_manager, - self._expiration_manager) - - def receive(self, ticket): - """See _Receiver.receive for specification.""" - if ticket.kind is interfaces.BackToFrontTicket.Kind.CONTINUATION: - self._ingestion_manager.consume(ticket.payload) - elif ticket.kind is interfaces.BackToFrontTicket.Kind.COMPLETION: - self._last_ticket_seen = True - if ticket.payload is None: - self._ingestion_manager.terminate() - else: - self._ingestion_manager.consume_and_terminate(ticket.payload) - - def reception_failure(self): - """See _Receiver.reception_failure for specification.""" - _reception_failure( - self._termination_manager, self._transmission_manager, - self._ingestion_manager, self._expiration_manager) - - -class _ReceptionManager(_interfaces.ReceptionManager): - """A ReceptionManager based around a _Receiver passed to it.""" - - def __init__(self, lock, receiver): - """Constructor. - - Args: - lock: The operation-servicing-wide lock object. - receiver: A _Receiver responsible for handling received tickets. - """ - self._lock = lock - self._receiver = receiver - - self._lowest_unseen_sequence_number = 0 - self._out_of_sequence_tickets = {} - self._completed_sequence_number = None - self._aborted = False - - def _sequence_failure(self, ticket): - """Determines a just-arrived ticket's sequential legitimacy. - - Args: - ticket: A just-arrived ticket. - - Returns: - True if the ticket is sequentially legitimate; False otherwise. - """ - if ticket.sequence_number < self._lowest_unseen_sequence_number: - return True - elif ticket.sequence_number in self._out_of_sequence_tickets: - return True - elif (self._completed_sequence_number is not None and - self._completed_sequence_number <= ticket.sequence_number): - return True - else: - return False - - def _process(self, ticket): - """Process those tickets ready to be processed. - - Args: - ticket: A just-arrived ticket the sequence number of which matches this - _ReceptionManager's _lowest_unseen_sequence_number field. - """ - while True: - completed = self._receiver.receive(ticket) - if completed: - self._out_of_sequence_tickets.clear() - self._completed_sequence_number = ticket.sequence_number - self._lowest_unseen_sequence_number = ticket.sequence_number + 1 - return - else: - next_ticket = self._out_of_sequence_tickets.pop( - ticket.sequence_number + 1, None) - if next_ticket is None: - self._lowest_unseen_sequence_number = ticket.sequence_number + 1 - return - else: - ticket = next_ticket - - def receive_ticket(self, ticket): - """See _interfaces.ReceptionManager.receive_ticket for specification.""" - with self._lock: - if self._aborted: - return - elif self._sequence_failure(ticket): - self._receiver.reception_failure() - self._aborted = True - elif self._receiver.abort_if_abortive(ticket): - self._aborted = True - elif ticket.sequence_number == self._lowest_unseen_sequence_number: - self._process(ticket) - else: - self._out_of_sequence_tickets[ticket.sequence_number] = ticket - - -def front_reception_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Creates a _interfaces.ReceptionManager for front-side use. - - Args: - lock: The operation-servicing-wide lock object. - termination_manager: The operation's _interfaces.TerminationManager. - transmission_manager: The operation's _interfaces.TransmissionManager. - ingestion_manager: The operation's _interfaces.IngestionManager. - expiration_manager: The operation's _interfaces.ExpirationManager. - - Returns: - A _interfaces.ReceptionManager appropriate for front-side use. - """ - return _ReceptionManager( - lock, _FrontReceiver( - termination_manager, transmission_manager, ingestion_manager, - expiration_manager)) - - -def back_reception_manager( - lock, termination_manager, transmission_manager, ingestion_manager, - expiration_manager): - """Creates a _interfaces.ReceptionManager for back-side use. - - Args: - lock: The operation-servicing-wide lock object. - termination_manager: The operation's _interfaces.TerminationManager. - transmission_manager: The operation's _interfaces.TransmissionManager. - ingestion_manager: The operation's _interfaces.IngestionManager. - expiration_manager: The operation's _interfaces.ExpirationManager. - - Returns: - A _interfaces.ReceptionManager appropriate for back-side use. - """ - return _ReceptionManager( - lock, _BackReceiver( - termination_manager, transmission_manager, ingestion_manager, - expiration_manager)) diff --git a/src/python/src/grpc/framework/base/_termination.py b/src/python/src/grpc/framework/base/_termination.py deleted file mode 100644 index ddcbc60293..0000000000 --- a/src/python/src/grpc/framework/base/_termination.py +++ /dev/null @@ -1,204 +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. - -"""State and behavior for operation termination.""" - -import enum - -from grpc.framework.base import _constants -from grpc.framework.base import _interfaces -from grpc.framework.base import interfaces -from grpc.framework.foundation import callable_util - -_CALLBACK_EXCEPTION_LOG_MESSAGE = 'Exception calling termination callback!' - - -@enum.unique -class _Requirement(enum.Enum): - """Symbols indicating events required for termination.""" - - EMISSION = 'emission' - TRANSMISSION = 'transmission' - INGESTION = 'ingestion' - -_FRONT_NOT_LISTENING_REQUIREMENTS = (_Requirement.TRANSMISSION,) -_BACK_NOT_LISTENING_REQUIREMENTS = ( - _Requirement.EMISSION, _Requirement.INGESTION,) -_LISTENING_REQUIREMENTS = ( - _Requirement.TRANSMISSION, _Requirement.INGESTION,) - - -class _TerminationManager(_interfaces.TerminationManager): - """An implementation of _interfaces.TerminationManager.""" - - def __init__( - self, work_pool, utility_pool, action, requirements, local_failure): - """Constructor. - - Args: - work_pool: A thread pool in which customer work will be done. - utility_pool: A thread pool in which work utility work will be done. - action: An action to call on operation termination. - requirements: A combination of _Requirement values identifying what - must finish for the operation to be considered completed. - local_failure: An interfaces.Outcome specifying what constitutes local - failure of customer work. - """ - self._work_pool = work_pool - self._utility_pool = utility_pool - self._action = action - self._local_failure = local_failure - self._has_locally_failed = False - self._expiration_manager = None - - self._outstanding_requirements = set(requirements) - self._outcome = None - self._callbacks = [] - - def set_expiration_manager(self, expiration_manager): - self._expiration_manager = expiration_manager - - def _terminate(self, outcome): - """Terminates the operation. - - Args: - outcome: An interfaces.Outcome describing the outcome of the operation. - """ - self._expiration_manager.abort() - self._outstanding_requirements = None - callbacks = list(self._callbacks) - self._callbacks = None - self._outcome = outcome - - act = callable_util.with_exceptions_logged( - self._action, _constants.INTERNAL_ERROR_LOG_MESSAGE) - - if self._has_locally_failed: - self._utility_pool.submit(act, outcome) - else: - def call_callbacks_and_act(callbacks, outcome): - for callback in callbacks: - callback_outcome = callable_util.call_logging_exceptions( - callback, _CALLBACK_EXCEPTION_LOG_MESSAGE, outcome) - if callback_outcome.exception is not None: - outcome = self._local_failure - break - self._utility_pool.submit(act, outcome) - - self._work_pool.submit(callable_util.with_exceptions_logged( - call_callbacks_and_act, - _constants.INTERNAL_ERROR_LOG_MESSAGE), - callbacks, outcome) - - def is_active(self): - """See _interfaces.TerminationManager.is_active for specification.""" - return self._outstanding_requirements is not None - - def add_callback(self, callback): - """See _interfaces.TerminationManager.add_callback for specification.""" - if not self._has_locally_failed: - if self._outstanding_requirements is None: - self._work_pool.submit( - callable_util.with_exceptions_logged( - callback, _CALLBACK_EXCEPTION_LOG_MESSAGE), self._outcome) - else: - self._callbacks.append(callback) - - def emission_complete(self): - """See superclass method for specification.""" - if self._outstanding_requirements is not None: - self._outstanding_requirements.discard(_Requirement.EMISSION) - if not self._outstanding_requirements: - self._terminate(interfaces.Outcome.COMPLETED) - - def transmission_complete(self): - """See superclass method for specification.""" - if self._outstanding_requirements is not None: - self._outstanding_requirements.discard(_Requirement.TRANSMISSION) - if not self._outstanding_requirements: - self._terminate(interfaces.Outcome.COMPLETED) - - def ingestion_complete(self): - """See superclass method for specification.""" - if self._outstanding_requirements is not None: - self._outstanding_requirements.discard(_Requirement.INGESTION) - if not self._outstanding_requirements: - self._terminate(interfaces.Outcome.COMPLETED) - - def abort(self, outcome): - """See _interfaces.TerminationManager.abort for specification.""" - if outcome is self._local_failure: - self._has_failed_locally = True - if self._outstanding_requirements is not None: - self._terminate(outcome) - - -def front_termination_manager( - work_pool, utility_pool, action, subscription_kind): - """Creates a TerminationManager appropriate for front-side use. - - Args: - work_pool: A thread pool in which customer work will be done. - utility_pool: A thread pool in which work utility work will be done. - action: An action to call on operation termination. - subscription_kind: An interfaces.ServicedSubscription.Kind value. - - Returns: - A TerminationManager appropriate for front-side use. - """ - if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: - requirements = _FRONT_NOT_LISTENING_REQUIREMENTS - else: - requirements = _LISTENING_REQUIREMENTS - - return _TerminationManager( - work_pool, utility_pool, action, requirements, - interfaces.Outcome.SERVICED_FAILURE) - - -def back_termination_manager(work_pool, utility_pool, action, subscription_kind): - """Creates a TerminationManager appropriate for back-side use. - - Args: - work_pool: A thread pool in which customer work will be done. - utility_pool: A thread pool in which work utility work will be done. - action: An action to call on operation termination. - subscription_kind: An interfaces.ServicedSubscription.Kind value. - - Returns: - A TerminationManager appropriate for back-side use. - """ - if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: - requirements = _BACK_NOT_LISTENING_REQUIREMENTS - else: - requirements = _LISTENING_REQUIREMENTS - - return _TerminationManager( - work_pool, utility_pool, action, requirements, - interfaces.Outcome.SERVICER_FAILURE) diff --git a/src/python/src/grpc/framework/base/_transmission.py b/src/python/src/grpc/framework/base/_transmission.py deleted file mode 100644 index 6845129234..0000000000 --- a/src/python/src/grpc/framework/base/_transmission.py +++ /dev/null @@ -1,429 +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. - -"""State and behavior for ticket transmission during an operation.""" - -import abc - -from grpc.framework.base import _constants -from grpc.framework.base import _interfaces -from grpc.framework.base import interfaces -from grpc.framework.foundation import callable_util - -_TRANSMISSION_EXCEPTION_LOG_MESSAGE = 'Exception during transmission!' - -_FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES = ( - interfaces.Outcome.SERVICER_FAILURE, - ) -_BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES = ( - interfaces.Outcome.CANCELLED, - interfaces.Outcome.SERVICED_FAILURE, - ) - -_ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND = { - interfaces.Outcome.CANCELLED: - interfaces.FrontToBackTicket.Kind.CANCELLATION, - interfaces.Outcome.EXPIRED: - interfaces.FrontToBackTicket.Kind.EXPIRATION, - interfaces.Outcome.RECEPTION_FAILURE: - interfaces.FrontToBackTicket.Kind.RECEPTION_FAILURE, - interfaces.Outcome.TRANSMISSION_FAILURE: - interfaces.FrontToBackTicket.Kind.TRANSMISSION_FAILURE, - interfaces.Outcome.SERVICED_FAILURE: - interfaces.FrontToBackTicket.Kind.SERVICED_FAILURE, - interfaces.Outcome.SERVICER_FAILURE: - interfaces.FrontToBackTicket.Kind.SERVICER_FAILURE, -} - -_ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND = { - interfaces.Outcome.CANCELLED: - interfaces.BackToFrontTicket.Kind.CANCELLATION, - interfaces.Outcome.EXPIRED: - interfaces.BackToFrontTicket.Kind.EXPIRATION, - interfaces.Outcome.RECEPTION_FAILURE: - interfaces.BackToFrontTicket.Kind.RECEPTION_FAILURE, - interfaces.Outcome.TRANSMISSION_FAILURE: - interfaces.BackToFrontTicket.Kind.TRANSMISSION_FAILURE, - interfaces.Outcome.SERVICED_FAILURE: - interfaces.BackToFrontTicket.Kind.SERVICED_FAILURE, - interfaces.Outcome.SERVICER_FAILURE: - interfaces.BackToFrontTicket.Kind.SERVICER_FAILURE, -} - - -class _Ticketizer(object): - """Common specification of different ticket-creating behavior.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def ticketize(self, operation_id, sequence_number, payload, complete): - """Creates a ticket indicating ordinary operation progress. - - Args: - operation_id: The operation ID for the current operation. - sequence_number: A sequence number for the ticket. - payload: A customer payload object. May be None if sequence_number is - zero or complete is true. - complete: A boolean indicating whether or not the ticket should describe - itself as (but for a later indication of operation abortion) the last - ticket to be sent. - - Returns: - An object of an appropriate type suitable for transmission to the other - side of the operation. - """ - raise NotImplementedError() - - @abc.abstractmethod - def ticketize_abortion(self, operation_id, sequence_number, outcome): - """Creates a ticket indicating that the operation is aborted. - - Args: - operation_id: The operation ID for the current operation. - sequence_number: A sequence number for the ticket. - outcome: An interfaces.Outcome value describing the operation abortion. - - Returns: - An object of an appropriate type suitable for transmission to the other - side of the operation, or None if transmission is not appropriate for - the given outcome. - """ - raise NotImplementedError() - - -class _FrontTicketizer(_Ticketizer): - """Front-side ticket-creating behavior.""" - - def __init__(self, name, subscription_kind, trace_id, timeout): - """Constructor. - - Args: - name: The name of the operation. - subscription_kind: An interfaces.ServicedSubscription.Kind value - describing the interest the front has in tickets sent from the back. - trace_id: A uuid.UUID identifying a set of related operations to which - this operation belongs. - timeout: A length of time in seconds to allow for the entire operation. - """ - self._name = name - self._subscription_kind = subscription_kind - self._trace_id = trace_id - self._timeout = timeout - - def ticketize(self, operation_id, sequence_number, payload, complete): - """See _Ticketizer.ticketize for specification.""" - if sequence_number: - if complete: - kind = interfaces.FrontToBackTicket.Kind.COMPLETION - else: - kind = interfaces.FrontToBackTicket.Kind.CONTINUATION - return interfaces.FrontToBackTicket( - operation_id, sequence_number, kind, self._name, - self._subscription_kind, self._trace_id, payload, self._timeout) - else: - if complete: - kind = interfaces.FrontToBackTicket.Kind.ENTIRE - else: - kind = interfaces.FrontToBackTicket.Kind.COMMENCEMENT - return interfaces.FrontToBackTicket( - operation_id, 0, kind, self._name, self._subscription_kind, - self._trace_id, payload, self._timeout) - - def ticketize_abortion(self, operation_id, sequence_number, outcome): - """See _Ticketizer.ticketize_abortion for specification.""" - if outcome in _FRONT_TO_BACK_NO_TRANSMISSION_OUTCOMES: - return None - else: - kind = _ABORTION_OUTCOME_TO_FRONT_TO_BACK_TICKET_KIND[outcome] - return interfaces.FrontToBackTicket( - operation_id, sequence_number, kind, None, None, None, None, None) - - -class _BackTicketizer(_Ticketizer): - """Back-side ticket-creating behavior.""" - - def ticketize(self, operation_id, sequence_number, payload, complete): - """See _Ticketizer.ticketize for specification.""" - if complete: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - return interfaces.BackToFrontTicket( - operation_id, sequence_number, kind, payload) - - def ticketize_abortion(self, operation_id, sequence_number, outcome): - """See _Ticketizer.ticketize_abortion for specification.""" - if outcome in _BACK_TO_FRONT_NO_TRANSMISSION_OUTCOMES: - return None - else: - kind = _ABORTION_OUTCOME_TO_BACK_TO_FRONT_TICKET_KIND[outcome] - return interfaces.BackToFrontTicket( - operation_id, sequence_number, kind, None) - - -class TransmissionManager(_interfaces.TransmissionManager): - """A _interfaces.TransmissionManager on which other managers may be set.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_ingestion_and_expiration_managers( - self, ingestion_manager, expiration_manager): - """Sets two of the other managers with which this manager may interact. - - Args: - ingestion_manager: The _interfaces.IngestionManager associated with the - current operation. - expiration_manager: The _interfaces.ExpirationManager associated with the - current operation. - """ - raise NotImplementedError() - - -class _EmptyTransmissionManager(TransmissionManager): - """A completely no-operative _interfaces.TransmissionManager.""" - - def set_ingestion_and_expiration_managers( - self, ingestion_manager, expiration_manager): - """See overriden method for specification.""" - - def inmit(self, emission, complete): - """See _interfaces.TransmissionManager.inmit for specification.""" - - def abort(self, outcome): - """See _interfaces.TransmissionManager.abort for specification.""" - - -class _TransmittingTransmissionManager(TransmissionManager): - """A TransmissionManager implementation that sends tickets.""" - - def __init__( - self, lock, pool, callback, operation_id, ticketizer, - termination_manager): - """Constructor. - - Args: - lock: The operation-servicing-wide lock object. - pool: A thread pool in which the work of transmitting tickets will be - performed. - callback: A callable that accepts tickets and sends them to the other side - of the operation. - operation_id: The operation's ID. - ticketizer: A _Ticketizer for ticket creation. - termination_manager: The _interfaces.TerminationManager associated with - this operation. - """ - self._lock = lock - self._pool = pool - self._callback = callback - self._operation_id = operation_id - self._ticketizer = ticketizer - self._termination_manager = termination_manager - self._ingestion_manager = None - self._expiration_manager = None - - self._emissions = [] - self._emission_complete = False - self._outcome = None - self._lowest_unused_sequence_number = 0 - self._transmitting = False - - def set_ingestion_and_expiration_managers( - self, ingestion_manager, expiration_manager): - """See overridden method for specification.""" - self._ingestion_manager = ingestion_manager - self._expiration_manager = expiration_manager - - def _lead_ticket(self, emission, complete): - """Creates a ticket suitable for leading off the transmission loop. - - Args: - emission: A customer payload object to be sent to the other side of the - operation. - complete: Whether or not the sequence of customer payloads ends with - the passed object. - - Returns: - A ticket with which to lead off the transmission loop. - """ - sequence_number = self._lowest_unused_sequence_number - self._lowest_unused_sequence_number += 1 - return self._ticketizer.ticketize( - self._operation_id, sequence_number, emission, complete) - - def _abortive_response_ticket(self, outcome): - """Creates a ticket indicating operation abortion. - - Args: - outcome: An interfaces.Outcome value describing operation abortion. - - Returns: - A ticket indicating operation abortion. - """ - ticket = self._ticketizer.ticketize_abortion( - self._operation_id, self._lowest_unused_sequence_number, outcome) - if ticket is None: - return None - else: - self._lowest_unused_sequence_number += 1 - return ticket - - def _next_ticket(self): - """Creates the next ticket to be sent to the other side of the operation. - - Returns: - A (completed, ticket) tuple comprised of a boolean indicating whether or - not the sequence of tickets has completed normally and a ticket to send - to the other side if the sequence of tickets hasn't completed. The tuple - will never have both a True first element and a non-None second element. - """ - if self._emissions is None: - return False, None - elif self._outcome is None: - if self._emissions: - payload = self._emissions.pop(0) - complete = self._emission_complete and not self._emissions - sequence_number = self._lowest_unused_sequence_number - self._lowest_unused_sequence_number += 1 - return complete, self._ticketizer.ticketize( - self._operation_id, sequence_number, payload, complete) - else: - return self._emission_complete, None - else: - ticket = self._abortive_response_ticket(self._outcome) - self._emissions = None - return False, None if ticket is None else ticket - - def _transmit(self, ticket): - """Commences the transmission loop sending tickets. - - Args: - ticket: A ticket to be sent to the other side of the operation. - """ - def transmit(ticket): - while True: - transmission_outcome = callable_util.call_logging_exceptions( - self._callback, _TRANSMISSION_EXCEPTION_LOG_MESSAGE, ticket) - if transmission_outcome.exception is None: - with self._lock: - complete, ticket = self._next_ticket() - if ticket is None: - if complete: - self._termination_manager.transmission_complete() - self._transmitting = False - return - else: - with self._lock: - self._emissions = None - self._termination_manager.abort( - interfaces.Outcome.TRANSMISSION_FAILURE) - self._ingestion_manager.abort() - self._expiration_manager.abort() - self._transmitting = False - return - - self._pool.submit(callable_util.with_exceptions_logged( - transmit, _constants.INTERNAL_ERROR_LOG_MESSAGE), ticket) - self._transmitting = True - - def inmit(self, emission, complete): - """See _interfaces.TransmissionManager.inmit for specification.""" - if self._emissions is not None and self._outcome is None: - self._emission_complete = complete - if self._transmitting: - self._emissions.append(emission) - else: - self._transmit(self._lead_ticket(emission, complete)) - - def abort(self, outcome): - """See _interfaces.TransmissionManager.abort for specification.""" - if self._emissions is not None and self._outcome is None: - self._outcome = outcome - if not self._transmitting: - ticket = self._abortive_response_ticket(outcome) - self._emissions = None - if ticket is not None: - self._transmit(ticket) - - -def front_transmission_manager( - lock, pool, callback, operation_id, name, subscription_kind, trace_id, - timeout, termination_manager): - """Creates a TransmissionManager appropriate for front-side use. - - Args: - lock: The operation-servicing-wide lock object. - pool: A thread pool in which the work of transmitting tickets will be - performed. - callback: A callable that accepts tickets and sends them to the other side - of the operation. - operation_id: The operation's ID. - name: The name of the operation. - subscription_kind: An interfaces.ServicedSubscription.Kind value - describing the interest the front has in tickets sent from the back. - trace_id: A uuid.UUID identifying a set of related operations to which - this operation belongs. - timeout: A length of time in seconds to allow for the entire operation. - termination_manager: The _interfaces.TerminationManager associated with - this operation. - - Returns: - A TransmissionManager appropriate for front-side use. - """ - return _TransmittingTransmissionManager( - lock, pool, callback, operation_id, _FrontTicketizer( - name, subscription_kind, trace_id, timeout), - termination_manager) - - -def back_transmission_manager( - lock, pool, callback, operation_id, termination_manager, - subscription_kind): - """Creates a TransmissionManager appropriate for back-side use. - - Args: - lock: The operation-servicing-wide lock object. - pool: A thread pool in which the work of transmitting tickets will be - performed. - callback: A callable that accepts tickets and sends them to the other side - of the operation. - operation_id: The operation's ID. - termination_manager: The _interfaces.TerminationManager associated with - this operation. - subscription_kind: An interfaces.ServicedSubscription.Kind value - describing the interest the front has in tickets sent from the back. - - Returns: - A TransmissionManager appropriate for back-side use. - """ - if subscription_kind is interfaces.ServicedSubscription.Kind.NONE: - return _EmptyTransmissionManager() - else: - return _TransmittingTransmissionManager( - lock, pool, callback, operation_id, _BackTicketizer(), - termination_manager) diff --git a/src/python/src/grpc/framework/base/exceptions.py b/src/python/src/grpc/framework/base/exceptions.py deleted file mode 100644 index b8f4752184..0000000000 --- a/src/python/src/grpc/framework/base/exceptions.py +++ /dev/null @@ -1,34 +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. - -"""Exceptions defined and used by the base layer of RPC Framework.""" - - -class NoSuchMethodError(Exception): - """Indicates that an operation with an unrecognized name has been called.""" diff --git a/src/python/src/grpc/framework/base/implementations.py b/src/python/src/grpc/framework/base/implementations.py deleted file mode 100644 index 5656f9f981..0000000000 --- a/src/python/src/grpc/framework/base/implementations.py +++ /dev/null @@ -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. - -"""Entry points into the ticket-exchange-based base layer implementation.""" - -# interfaces is referenced from specification in this module. -from grpc.framework.base import _ends -from grpc.framework.base import interfaces # pylint: disable=unused-import - - -def front_link(work_pool, transmission_pool, utility_pool): - """Factory function for creating interfaces.FrontLinks. - - Args: - work_pool: A thread pool to be used for doing work within the created - FrontLink object. - transmission_pool: A thread pool to be used within the created FrontLink - object for transmitting values to a joined RearLink object. - utility_pool: A thread pool to be used within the created FrontLink object - for utility tasks. - - Returns: - An interfaces.FrontLink. - """ - return _ends.FrontLink(work_pool, transmission_pool, utility_pool) - - -def back_link( - servicer, work_pool, transmission_pool, utility_pool, default_timeout, - maximum_timeout): - """Factory function for creating interfaces.BackLinks. - - Args: - servicer: An interfaces.Servicer for servicing operations. - work_pool: A thread pool to be used for doing work within the created - BackLink object. - transmission_pool: A thread pool to be used within the created BackLink - object for transmitting values to a joined ForeLink object. - utility_pool: A thread pool to be used within the created BackLink object - for utility tasks. - default_timeout: A length of time in seconds to be used as the default - time alloted for a single operation. - maximum_timeout: A length of time in seconds to be used as the maximum - time alloted for a single operation. - - Returns: - An interfaces.BackLink. - """ - return _ends.BackLink( - servicer, work_pool, transmission_pool, utility_pool, default_timeout, - maximum_timeout) diff --git a/src/python/src/grpc/framework/base/implementations_test.py b/src/python/src/grpc/framework/base/implementations_test.py deleted file mode 100644 index 72087f4456..0000000000 --- a/src/python/src/grpc/framework/base/implementations_test.py +++ /dev/null @@ -1,80 +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. - -"""Tests for grpc.framework.base.implementations.""" - -import unittest - -from grpc.framework.base import implementations -from grpc.framework.base import interfaces_test_case -from grpc.framework.base import util -from grpc.framework.foundation import logging_pool - -POOL_MAX_WORKERS = 10 -DEFAULT_TIMEOUT = 30 -MAXIMUM_TIMEOUT = 60 - - -class ImplementationsTest( - interfaces_test_case.FrontAndBackTest, unittest.TestCase): - - def setUp(self): - self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) - self.front = implementations.front_link( - self.front_work_pool, self.front_transmission_pool, - self.front_utility_pool) - self.back = implementations.back_link( - self.test_servicer, self.back_work_pool, self.back_transmission_pool, - self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) - self.front.join_rear_link(self.back) - self.back.join_fore_link(self.front) - - def tearDown(self): - util.wait_for_idle(self.back) - util.wait_for_idle(self.front) - self.memory_transmission_pool.shutdown(wait=True) - self.front_work_pool.shutdown(wait=True) - self.front_transmission_pool.shutdown(wait=True) - self.front_utility_pool.shutdown(wait=True) - self.back_work_pool.shutdown(wait=True) - self.back_transmission_pool.shutdown(wait=True) - self.back_utility_pool.shutdown(wait=True) - self.test_pool.shutdown(wait=True) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/base/in_memory.py b/src/python/src/grpc/framework/base/in_memory.py deleted file mode 100644 index c92d0bc663..0000000000 --- a/src/python/src/grpc/framework/base/in_memory.py +++ /dev/null @@ -1,108 +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. - -"""In-memory implementations of base layer interfaces.""" - -import threading - -from grpc.framework.base import _constants -from grpc.framework.base import interfaces -from grpc.framework.foundation import callable_util - - -class _Serializer(object): - """A utility for serializing values that may arrive concurrently.""" - - def __init__(self, pool): - self._lock = threading.Lock() - self._pool = pool - self._sink = None - self._spinning = False - self._values = [] - - def _spin(self, sink, value): - while True: - sink(value) - with self._lock: - if self._sink is None or not self._values: - self._spinning = False - return - else: - sink, value = self._sink, self._values.pop(0) - - def set_sink(self, sink): - with self._lock: - self._sink = sink - if sink is not None and self._values and not self._spinning: - self._spinning = True - self._pool.submit( - callable_util.with_exceptions_logged( - self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), - sink, self._values.pop(0)) - - def add_value(self, value): - with self._lock: - if self._sink and not self._spinning: - self._spinning = True - self._pool.submit( - callable_util.with_exceptions_logged( - self._spin, _constants.INTERNAL_ERROR_LOG_MESSAGE), - self._sink, value) - else: - self._values.append(value) - - -class Link(interfaces.ForeLink, interfaces.RearLink): - """A trivial implementation of interfaces.ForeLink and interfaces.RearLink.""" - - def __init__(self, pool): - """Constructor. - - Args: - pool: A thread pool to be used for serializing ticket exchange in each - direction. - """ - self._front_to_back = _Serializer(pool) - self._back_to_front = _Serializer(pool) - - def join_fore_link(self, fore_link): - """See interfaces.RearLink.join_fore_link for specification.""" - self._back_to_front.set_sink(fore_link.accept_back_to_front_ticket) - - def join_rear_link(self, rear_link): - """See interfaces.ForeLink.join_rear_link for specification.""" - self._front_to_back.set_sink(rear_link.accept_front_to_back_ticket) - - def accept_front_to_back_ticket(self, ticket): - """See interfaces.ForeLink.accept_front_to_back_ticket for specification.""" - self._front_to_back.add_value(ticket) - - def accept_back_to_front_ticket(self, ticket): - """See interfaces.RearLink.accept_back_to_front_ticket for specification.""" - self._back_to_front.add_value(ticket) diff --git a/src/python/src/grpc/framework/base/interfaces.py b/src/python/src/grpc/framework/base/interfaces.py deleted file mode 100644 index e22c10d975..0000000000 --- a/src/python/src/grpc/framework/base/interfaces.py +++ /dev/null @@ -1,363 +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. - -"""Interfaces defined and used by the base layer of RPC Framework.""" - -import abc -import collections -import enum - -# stream is referenced from specification in this module. -from grpc.framework.foundation import stream # pylint: disable=unused-import - - -@enum.unique -class Outcome(enum.Enum): - """Operation outcomes.""" - - COMPLETED = 'completed' - CANCELLED = 'cancelled' - EXPIRED = 'expired' - RECEPTION_FAILURE = 'reception failure' - TRANSMISSION_FAILURE = 'transmission failure' - SERVICER_FAILURE = 'servicer failure' - SERVICED_FAILURE = 'serviced failure' - - -class OperationContext(object): - """Provides operation-related information and action. - - Attributes: - trace_id: A uuid.UUID identifying a particular set of related operations. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def is_active(self): - """Describes whether the operation is active or has terminated.""" - raise NotImplementedError() - - @abc.abstractmethod - def add_termination_callback(self, callback): - """Adds a function to be called upon operation termination. - - Args: - callback: A callable that will be passed an Outcome value. - """ - raise NotImplementedError() - - @abc.abstractmethod - def time_remaining(self): - """Describes the length of allowed time remaining for the operation. - - Returns: - A nonnegative float indicating the length of allowed time in seconds - remaining for the operation to complete before it is considered to have - timed out. - """ - raise NotImplementedError() - - @abc.abstractmethod - def fail(self, exception): - """Indicates that the operation has failed. - - Args: - exception: An exception germane to the operation failure. May be None. - """ - raise NotImplementedError() - - -class Servicer(object): - """Interface for service implementations.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, name, context, output_consumer): - """Services an operation. - - Args: - name: The name of the operation. - context: A ServicerContext object affording contextual information and - actions. - output_consumer: A stream.Consumer that will accept output values of - the operation. - - Returns: - A stream.Consumer that will accept input values for the operation. - - Raises: - exceptions.NoSuchMethodError: If this Servicer affords no method with the - given name. - abandonment.Abandoned: If the operation has been aborted and there no - longer is any reason to service the operation. - """ - raise NotImplementedError() - - -class Operation(object): - """Representation of an in-progress operation. - - Attributes: - consumer: A stream.Consumer into which payloads constituting the operation's - input may be passed. - context: An OperationContext affording information and action about the - operation. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def cancel(self): - """Cancels this operation.""" - raise NotImplementedError() - - -class ServicedIngestor(object): - """Responsible for accepting the result of an operation.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def consumer(self, operation_context): - """Affords a consumer to which operation results will be passed. - - Args: - operation_context: An OperationContext object for the current operation. - - Returns: - A stream.Consumer to which the results of the current operation will be - passed. - - Raises: - abandonment.Abandoned: If the operation has been aborted and there no - longer is any reason to service the operation. - """ - raise NotImplementedError() - - -class ServicedSubscription(object): - """A sum type representing a serviced's interest in an operation. - - Attributes: - kind: A Kind value. - ingestor: A ServicedIngestor. Must be present if kind is Kind.FULL. Must - be None if kind is Kind.TERMINATION_ONLY or Kind.NONE. - """ - __metaclass__ = abc.ABCMeta - - @enum.unique - class Kind(enum.Enum): - """Kinds of subscription.""" - - FULL = 'full' - TERMINATION_ONLY = 'termination only' - NONE = 'none' - - -class End(object): - """Common type for entry-point objects on both sides of an operation.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def operation_stats(self): - """Reports the number of terminated operations broken down by outcome. - - Returns: - A dictionary from Outcome value to an integer identifying the number - of operations that terminated with that outcome. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_idle_action(self, action): - """Adds an action to be called when this End has no ongoing operations. - - Args: - action: A callable that accepts no arguments. - """ - raise NotImplementedError() - - -class Front(End): - """Clientish objects that afford the invocation of operations.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def operate( - self, name, payload, complete, timeout, subscription, trace_id): - """Commences an operation. - - Args: - name: The name of the method invoked for the operation. - payload: An initial payload for the operation. May be None. - complete: A boolean indicating whether or not additional payloads to be - sent to the servicer may be supplied after this call. - timeout: A length of time in seconds to allow for the operation. - subscription: A ServicedSubscription for the operation. - trace_id: A uuid.UUID identifying a set of related operations to which - this operation belongs. - - Returns: - An Operation object affording information and action about the operation - in progress. - """ - raise NotImplementedError() - - -class Back(End): - """Serverish objects that perform the work of operations.""" - __metaclass__ = abc.ABCMeta - - -class FrontToBackTicket( - collections.namedtuple( - 'FrontToBackTicket', - ['operation_id', 'sequence_number', 'kind', 'name', 'subscription', - 'trace_id', 'payload', 'timeout'])): - """A sum type for all values sent from a front to a back. - - Attributes: - operation_id: A unique-with-respect-to-equality hashable object identifying - a particular operation. - sequence_number: A zero-indexed integer sequence number identifying the - ticket's place among all the tickets sent from front to back for this - particular operation. Must be zero if kind is Kind.COMMENCEMENT or - Kind.ENTIRE. Must be positive for any other kind. - kind: A Kind value describing the overall kind of ticket. - name: The name of an operation. Must be present if kind is Kind.COMMENCEMENT - or Kind.ENTIRE. Must be None for any other kind. - subscription: An ServicedSubscription.Kind value describing the interest - the front has in tickets sent from the back. Must be present if - kind is Kind.COMMENCEMENT or Kind.ENTIRE. Must be None for any other kind. - trace_id: A uuid.UUID identifying a set of related operations to which this - operation belongs. May be None. - payload: A customer payload object. Must be present if kind is - Kind.CONTINUATION. Must be None if kind is Kind.CANCELLATION. May be None - for any other kind. - timeout: An optional length of time (measured from the beginning of the - operation) to allow for the entire operation. If None, a default value on - the back will be used. If present and excessively large, the back may - limit the operation to a smaller duration of its choice. May be present - for any ticket kind; setting a value on a later ticket allows fronts - to request time extensions (or even time reductions!) on in-progress - operations. - """ - - @enum.unique - class Kind(enum.Enum): - """Identifies the overall kind of a FrontToBackTicket.""" - - COMMENCEMENT = 'commencement' - CONTINUATION = 'continuation' - COMPLETION = 'completion' - ENTIRE = 'entire' - CANCELLATION = 'cancellation' - EXPIRATION = 'expiration' - SERVICER_FAILURE = 'servicer failure' - SERVICED_FAILURE = 'serviced failure' - RECEPTION_FAILURE = 'reception failure' - TRANSMISSION_FAILURE = 'transmission failure' - - -class BackToFrontTicket( - collections.namedtuple( - 'BackToFrontTicket', - ['operation_id', 'sequence_number', 'kind', 'payload'])): - """A sum type for all values sent from a back to a front. - - Attributes: - operation_id: A unique-with-respect-to-equality hashable object identifying - a particular operation. - sequence_number: A zero-indexed integer sequence number identifying the - ticket's place among all the tickets sent from back to front for this - particular operation. - kind: A Kind value describing the overall kind of ticket. - payload: A customer payload object. Must be present if kind is - Kind.CONTINUATION. May be None if kind is Kind.COMPLETION. Must be None - otherwise. - """ - - @enum.unique - class Kind(enum.Enum): - """Identifies the overall kind of a BackToFrontTicket.""" - - CONTINUATION = 'continuation' - COMPLETION = 'completion' - CANCELLATION = 'cancellation' - EXPIRATION = 'expiration' - SERVICER_FAILURE = 'servicer failure' - SERVICED_FAILURE = 'serviced failure' - RECEPTION_FAILURE = 'reception failure' - TRANSMISSION_FAILURE = 'transmission failure' - - -class ForeLink(object): - """Accepts back-to-front tickets and emits front-to-back tickets.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def accept_back_to_front_ticket(self, ticket): - """Accept a BackToFrontTicket. - - Args: - ticket: Any BackToFrontTicket. - """ - raise NotImplementedError() - - @abc.abstractmethod - def join_rear_link(self, rear_link): - """Mates this object with a peer with which it will exchange tickets.""" - raise NotImplementedError() - - -class RearLink(object): - """Accepts front-to-back tickets and emits back-to-front tickets.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def accept_front_to_back_ticket(self, ticket): - """Accepts a FrontToBackTicket. - - Args: - ticket: Any FrontToBackTicket. - """ - raise NotImplementedError() - - @abc.abstractmethod - def join_fore_link(self, fore_link): - """Mates this object with a peer with which it will exchange tickets.""" - raise NotImplementedError() - - -class FrontLink(Front, ForeLink): - """Clientish objects that operate by sending and receiving tickets.""" - __metaclass__ = abc.ABCMeta - - -class BackLink(Back, RearLink): - """Serverish objects that operate by sending and receiving tickets.""" - __metaclass__ = abc.ABCMeta diff --git a/src/python/src/grpc/framework/base/interfaces_test_case.py b/src/python/src/grpc/framework/base/interfaces_test_case.py deleted file mode 100644 index dec10c2924..0000000000 --- a/src/python/src/grpc/framework/base/interfaces_test_case.py +++ /dev/null @@ -1,307 +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. - -"""Abstract tests against the interfaces of the base layer of RPC Framework.""" - -import threading -import time - -from grpc.framework.base import interfaces -from grpc.framework.base import util -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_testing -from grpc.framework.foundation import stream_util - -TICK = 0.1 -SMALL_TIMEOUT = TICK * 50 -STREAM_LENGTH = 100 - -SYNCHRONOUS_ECHO = 'synchronous echo' -ASYNCHRONOUS_ECHO = 'asynchronous echo' -IMMEDIATE_FAILURE = 'immediate failure' -TRIGGERED_FAILURE = 'triggered failure' -WAIT_ON_CONDITION = 'wait on condition' - -EMPTY_OUTCOME_DICT = { - interfaces.Outcome.COMPLETED: 0, - interfaces.Outcome.CANCELLED: 0, - interfaces.Outcome.EXPIRED: 0, - interfaces.Outcome.RECEPTION_FAILURE: 0, - interfaces.Outcome.TRANSMISSION_FAILURE: 0, - interfaces.Outcome.SERVICER_FAILURE: 0, - interfaces.Outcome.SERVICED_FAILURE: 0, - } - - -def _synchronous_echo(output_consumer): - return stream_util.TransformingConsumer(lambda x: x, output_consumer) - - -class AsynchronousEcho(stream.Consumer): - """A stream.Consumer that echoes its input to another stream.Consumer.""" - - def __init__(self, output_consumer, pool): - self._lock = threading.Lock() - self._output_consumer = output_consumer - self._pool = pool - - self._queue = [] - self._spinning = False - - def _spin(self, value, complete): - while True: - if value: - if complete: - self._output_consumer.consume_and_terminate(value) - else: - self._output_consumer.consume(value) - elif complete: - self._output_consumer.terminate() - with self._lock: - if self._queue: - value, complete = self._queue.pop(0) - else: - self._spinning = False - return - - def consume(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, False)) - else: - self._spinning = True - self._pool.submit(self._spin, value, False) - - def terminate(self): - with self._lock: - if self._spinning: - self._queue.append((None, True)) - else: - self._spinning = True - self._pool.submit(self._spin, None, True) - - def consume_and_terminate(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, True)) - else: - self._spinning = True - self._pool.submit(self._spin, value, True) - - -class TestServicer(interfaces.Servicer): - """An interfaces.Servicer with instrumented for testing.""" - - def __init__(self, pool): - self._pool = pool - self.condition = threading.Condition() - self._released = False - - def service(self, name, context, output_consumer): - if name == SYNCHRONOUS_ECHO: - return _synchronous_echo(output_consumer) - elif name == ASYNCHRONOUS_ECHO: - return AsynchronousEcho(output_consumer, self._pool) - elif name == IMMEDIATE_FAILURE: - raise ValueError() - elif name == TRIGGERED_FAILURE: - raise NotImplementedError - elif name == WAIT_ON_CONDITION: - with self.condition: - while not self._released: - self.condition.wait() - return _synchronous_echo(output_consumer) - else: - raise NotImplementedError() - - def release(self): - with self.condition: - self._released = True - self.condition.notify_all() - - -class EasyServicedIngestor(interfaces.ServicedIngestor): - """A trivial implementation of interfaces.ServicedIngestor.""" - - def __init__(self, consumer): - self._consumer = consumer - - def consumer(self, operation_context): - """See interfaces.ServicedIngestor.consumer for specification.""" - return self._consumer - - -class FrontAndBackTest(object): - """A test suite usable against any joined Front and Back.""" - - # Pylint doesn't know that this is a unittest.TestCase mix-in. - # pylint: disable=invalid-name - - def testSimplestCall(self): - """Tests the absolute simplest call - a one-ticket fire-and-forget.""" - self.front.operate( - SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, - util.none_serviced_subscription(), 'test trace ID') - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) The ticket is still either in the front waiting to be transmitted - # or is somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the ticket bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) The ticket arrived at the back and the back completed the operation. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.COMPLETED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - # It's true that if the ticket had arrived at the back and the back had - # begun processing that wait_for_idle could hold test execution until the - # back completed the operation, but that doesn't really collapse the - # possibility space down to one solution. - - def testEntireEcho(self): - """Tests a very simple one-ticket-each-way round-trip.""" - test_payload = 'test payload' - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - self.front.operate( - ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, - 'test trace ID') - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual([(test_payload, True)], test_consumer.calls) - - def testBidirectionalStreamingEcho(self): - """Tests sending multiple tickets each way.""" - test_payload_template = 'test_payload: %03d' - test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - - for test_payload in test_payloads: - operation.consumer.consume(test_payload) - operation.consumer.terminate() - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual(test_payloads, test_consumer.values()) - - def testCancellation(self): - """Tests cancelling a long-lived operation.""" - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - operation.cancel() - - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) - util.wait_for_idle(self.back) - self.assertListEqual([], test_consumer.calls) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) Both tickets are still either in the front waiting to be transmitted - # or are somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the tickets bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. - # The back started processing based on the first ticket and then stopped - # upon receiving the cancellation ticket. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.CANCELLED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - - def testExpiration(self): - """Tests that operations time out.""" - timeout = TICK * 2 - allowance = TICK # How much extra time to - condition = threading.Condition() - test_payload = 'test payload' - subscription = util.termination_only_serviced_subscription() - start_time = time.time() - - outcome_cell = [None] - termination_time_cell = [None] - def termination_action(outcome): - with condition: - outcome_cell[0] = outcome - termination_time_cell[0] = time.time() - condition.notify() - - with condition: - operation = self.front.operate( - SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, - 'test trace ID') - operation.context.add_termination_callback(termination_action) - while outcome_cell[0] is None: - condition.wait() - - duration = termination_time_cell[0] - start_time - self.assertLessEqual(timeout, duration) - self.assertLess(duration, timeout + allowance) - self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) - util.wait_for_idle(self.back) - self.assertLessEqual( - 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/src/grpc/framework/base/null.py b/src/python/src/grpc/framework/base/null.py deleted file mode 100644 index 1e30d4557b..0000000000 --- a/src/python/src/grpc/framework/base/null.py +++ /dev/null @@ -1,56 +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. - -"""Null links that ignore tickets passed to them.""" - -from grpc.framework.base import interfaces - - -class _NullForeLink(interfaces.ForeLink): - """A do-nothing ForeLink.""" - - def accept_back_to_front_ticket(self, ticket): - pass - - def join_rear_link(self, rear_link): - raise NotImplementedError() - - -class _NullRearLink(interfaces.RearLink): - """A do-nothing RearLink.""" - - def accept_front_to_back_ticket(self, ticket): - pass - - def join_fore_link(self, fore_link): - raise NotImplementedError() - - -NULL_FORE_LINK = _NullForeLink() -NULL_REAR_LINK = _NullRearLink() diff --git a/src/python/src/grpc/framework/base/util.py b/src/python/src/grpc/framework/base/util.py deleted file mode 100644 index c832c826cf..0000000000 --- a/src/python/src/grpc/framework/base/util.py +++ /dev/null @@ -1,94 +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. - -"""Utilities helpful for working with the base layer of RPC Framework.""" - -import collections -import threading - -from grpc.framework.base import interfaces - - -class _ServicedSubscription( - collections.namedtuple('_ServicedSubscription', ['kind', 'ingestor']), - interfaces.ServicedSubscription): - """See interfaces.ServicedSubscription for specification.""" - -_NONE_SUBSCRIPTION = _ServicedSubscription( - interfaces.ServicedSubscription.Kind.NONE, None) -_TERMINATION_ONLY_SUBSCRIPTION = _ServicedSubscription( - interfaces.ServicedSubscription.Kind.TERMINATION_ONLY, None) - - -def none_serviced_subscription(): - """Creates a "none" interfaces.ServicedSubscription object. - - Returns: - An interfaces.ServicedSubscription indicating no subscription to an - operation's results (such as would be the case for a fire-and-forget - operation invocation). - """ - return _NONE_SUBSCRIPTION - - -def termination_only_serviced_subscription(): - """Creates a "termination only" interfaces.ServicedSubscription object. - - Returns: - An interfaces.ServicedSubscription indicating that the front-side customer - is interested only in the overall termination outcome of the operation - (such as completion or expiration) and would ignore the actual results of - the operation. - """ - return _TERMINATION_ONLY_SUBSCRIPTION - - -def full_serviced_subscription(ingestor): - """Creates a "full" interfaces.ServicedSubscription object. - - Args: - ingestor: An interfaces.ServicedIngestor. - - Returns: - An interfaces.ServicedSubscription object indicating a full - subscription. - """ - return _ServicedSubscription( - interfaces.ServicedSubscription.Kind.FULL, ingestor) - - -def wait_for_idle(end): - """Waits for an interfaces.End to complete all operations. - - Args: - end: Any interfaces.End. - """ - event = threading.Event() - end.add_idle_action(event.set) - event.wait() diff --git a/src/python/src/grpc/framework/common/__init__.py b/src/python/src/grpc/framework/common/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/common/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/common/cardinality.py b/src/python/src/grpc/framework/common/cardinality.py deleted file mode 100644 index 610425e803..0000000000 --- a/src/python/src/grpc/framework/common/cardinality.py +++ /dev/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. - -"""Defines an enum for classifying RPC methods by streaming semantics.""" - -import enum - - -@enum.unique -class Cardinality(enum.Enum): - """Describes the streaming semantics of an RPC method.""" - - UNARY_UNARY = 'request-unary/response-unary' - UNARY_STREAM = 'request-unary/response-streaming' - STREAM_UNARY = 'request-streaming/response-unary' - STREAM_STREAM = 'request-streaming/response-streaming' diff --git a/src/python/src/grpc/framework/common/style.py b/src/python/src/grpc/framework/common/style.py deleted file mode 100644 index 6ae694bdcb..0000000000 --- a/src/python/src/grpc/framework/common/style.py +++ /dev/null @@ -1,40 +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. - -"""Defines an enum for classifying RPC methods by control flow semantics.""" - -import enum - - -@enum.unique -class Service(enum.Enum): - """Describes the control flow style of RPC method implementation.""" - - INLINE = 'inline' - EVENT = 'event' diff --git a/src/python/src/grpc/framework/common/test_constants.py b/src/python/src/grpc/framework/common/test_constants.py deleted file mode 100644 index 3126d0d82c..0000000000 --- a/src/python/src/grpc/framework/common/test_constants.py +++ /dev/null @@ -1,43 +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. - -"""Constants shared among tests throughout RPC Framework.""" - -# Value for maximum duration in seconds of RPCs that may time out as part of a -# test. -SHORT_TIMEOUT = 4 -# Absurdly large value for maximum duration in seconds for should-not-time-out -# RPCs made during tests. -LONG_TIMEOUT = 3000 - -# The number of payloads to transmit in streaming tests. -STREAM_LENGTH = 200 - -# The size of thread pools to use in tests. -POOL_SIZE = 10 diff --git a/src/python/src/grpc/framework/common/test_control.py b/src/python/src/grpc/framework/common/test_control.py deleted file mode 100644 index 3960c4e649..0000000000 --- a/src/python/src/grpc/framework/common/test_control.py +++ /dev/null @@ -1,87 +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. - -"""Code for instructing systems under test to block or fail.""" - -import abc -import contextlib -import threading - - -class Control(object): - """An object that accepts program control from a system under test. - - Systems under test passed a Control should call its control() method - frequently during execution. The control() method may block, raise an - exception, or do nothing, all according to the enclosing test's desire for - the system under test to simulate hanging, failing, or functioning. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def control(self): - """Potentially does anything.""" - raise NotImplementedError() - - -class PauseFailControl(Control): - """A Control that can be used to pause or fail code under control.""" - - def __init__(self): - self._condition = threading.Condition() - self._paused = False - self._fail = False - - def control(self): - with self._condition: - if self._fail: - raise ValueError() - - while self._paused: - self._condition.wait() - - @contextlib.contextmanager - def pause(self): - """Pauses code under control while controlling code is in context.""" - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): - """Fails code under control while controlling code is in context.""" - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False diff --git a/src/python/src/grpc/framework/common/test_coverage.py b/src/python/src/grpc/framework/common/test_coverage.py deleted file mode 100644 index a7ed3582c4..0000000000 --- a/src/python/src/grpc/framework/common/test_coverage.py +++ /dev/null @@ -1,116 +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. - -"""Governs coverage for tests of RPCs throughout RPC Framework.""" - -import abc - -# This code is designed for use with the unittest module. -# pylint: disable=invalid-name - - -class Coverage(object): - """Specification of test coverage.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testSuccessfulUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSequentialInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/face/__init__.py b/src/python/src/grpc/framework/face/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/face/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/face/_calls.py b/src/python/src/grpc/framework/face/_calls.py deleted file mode 100644 index 87edeb0f0e..0000000000 --- a/src/python/src/grpc/framework/face/_calls.py +++ /dev/null @@ -1,422 +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. - -"""Utility functions for invoking RPCs.""" - -import sys -import threading - -from grpc.framework.base import interfaces as base_interfaces -from grpc.framework.base import util as base_util -from grpc.framework.face import _control -from grpc.framework.face import interfaces -from grpc.framework.foundation import callable_util -from grpc.framework.foundation import future - -_ITERATOR_EXCEPTION_LOG_MESSAGE = 'Exception iterating over requests!' -_DONE_CALLBACK_LOG_MESSAGE = 'Exception calling Future "done" callback!' - - -class _RendezvousServicedIngestor(base_interfaces.ServicedIngestor): - - def __init__(self, rendezvous): - self._rendezvous = rendezvous - - def consumer(self, operation_context): - return self._rendezvous - - -class _EventServicedIngestor(base_interfaces.ServicedIngestor): - - def __init__(self, result_consumer, abortion_callback): - self._result_consumer = result_consumer - self._abortion_callback = abortion_callback - - def consumer(self, operation_context): - operation_context.add_termination_callback( - _control.as_operation_termination_callback(self._abortion_callback)) - return self._result_consumer - - -def _rendezvous_subscription(rendezvous): - return base_util.full_serviced_subscription( - _RendezvousServicedIngestor(rendezvous)) - - -def _unary_event_subscription(completion_callback, abortion_callback): - return base_util.full_serviced_subscription( - _EventServicedIngestor( - _control.UnaryConsumer(completion_callback), abortion_callback)) - - -def _stream_event_subscription(result_consumer, abortion_callback): - return base_util.full_serviced_subscription( - _EventServicedIngestor(result_consumer, abortion_callback)) - - -# NOTE(nathaniel): This class has some extremely special semantics around -# cancellation that allow it to be used by both "blocking" APIs and "futures" -# APIs. -# -# Since futures.Future defines its own exception for cancellation, we want these -# objects, when returned by methods of a returning-Futures-from-other-methods -# object, to raise the same exception for cancellation. But that's weird in a -# blocking API - why should this object, also returned by methods of blocking -# APIs, raise exceptions from the "future" module? Should we do something like -# have this class be parameterized by the type of exception that it raises in -# cancellation circumstances? -# -# We don't have to take such a dramatic step: since blocking APIs define no -# cancellation semantics whatsoever, there is no supported way for -# blocking-API-users of these objects to cancel RPCs, and thus no supported way -# for them to see an exception the type of which would be weird to them. -# -# Bonus: in both blocking and futures APIs, this object still properly raises -# exceptions.CancellationError for any *server-side cancellation* of an RPC. -class _OperationCancellableIterator(interfaces.CancellableIterator): - """An interfaces.CancellableIterator for response-streaming operations.""" - - def __init__(self, rendezvous, operation): - self._lock = threading.Lock() - self._rendezvous = rendezvous - self._operation = operation - self._cancelled = False - - def __iter__(self): - return self - - def next(self): - with self._lock: - if self._cancelled: - raise future.CancelledError() - return next(self._rendezvous) - - def cancel(self): - with self._lock: - self._cancelled = True - self._operation.cancel() - self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED) - - -class _OperationFuture(future.Future): - """A future.Future interface to an operation.""" - - def __init__(self, rendezvous, operation): - self._condition = threading.Condition() - self._rendezvous = rendezvous - self._operation = operation - - self._cancelled = False - self._computed = False - self._payload = None - self._exception = None - self._traceback = None - self._callbacks = [] - - def cancel(self): - """See future.Future.cancel for specification.""" - with self._condition: - if not self._cancelled and not self._computed: - self._operation.cancel() - self._cancelled = True - self._condition.notify_all() - return False - - def cancelled(self): - """See future.Future.cancelled for specification.""" - with self._condition: - return self._cancelled - - def running(self): - """See future.Future.running for specification.""" - with self._condition: - return not self._cancelled and not self._computed - - def done(self): - """See future.Future.done for specification.""" - with self._condition: - return self._cancelled or self._computed - - def result(self, timeout=None): - """See future.Future.result for specification.""" - with self._condition: - if self._cancelled: - raise future.CancelledError() - if self._computed: - if self._payload is None: - raise self._exception # pylint: disable=raising-bad-type - else: - return self._payload - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._callbacks.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._condition: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - if self._payload is None: - raise self._exception # pylint: disable=raising-bad-type - else: - return self._payload - else: - raise future.TimeoutError() - - def exception(self, timeout=None): - """See future.Future.exception for specification.""" - with self._condition: - if self._cancelled: - raise future.CancelledError() - if self._computed: - return self._exception - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._callbacks.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._condition: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._exception - else: - raise future.TimeoutError() - - def traceback(self, timeout=None): - """See future.Future.traceback for specification.""" - with self._condition: - if self._cancelled: - raise future.CancelledError() - if self._computed: - return self._traceback - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._callbacks.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._condition: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._traceback - else: - raise future.TimeoutError() - - def add_done_callback(self, fn): - """See future.Future.add_done_callback for specification.""" - with self._condition: - if self._callbacks is not None: - self._callbacks.append(fn) - return - - callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self) - - def on_operation_termination(self, operation_outcome): - """Indicates to this object that the operation has terminated. - - Args: - operation_outcome: A base_interfaces.Outcome value indicating the - outcome of the operation. - """ - with self._condition: - cancelled = self._cancelled - if cancelled: - callbacks = list(self._callbacks) - self._callbacks = None - else: - rendezvous = self._rendezvous - - if not cancelled: - payload = None - exception = None - traceback = None - if operation_outcome == base_interfaces.Outcome.COMPLETED: - try: - payload = next(rendezvous) - except Exception as e: # pylint: disable=broad-except - exception = e - traceback = sys.exc_info()[2] - else: - try: - # We raise and then immediately catch in order to create a traceback. - raise _control.abortion_outcome_to_exception(operation_outcome) - except Exception as e: # pylint: disable=broad-except - exception = e - traceback = sys.exc_info()[2] - with self._condition: - if not self._cancelled: - self._computed = True - self._payload = payload - self._exception = exception - self._traceback = traceback - callbacks = list(self._callbacks) - self._callbacks = None - - for callback in callbacks: - callable_util.call_logging_exceptions( - callback, _DONE_CALLBACK_LOG_MESSAGE, self) - - -class _Call(interfaces.Call): - - def __init__(self, operation): - self._operation = operation - self.context = _control.RpcContext(operation.context) - - def cancel(self): - self._operation.cancel() - - -def blocking_value_in_value_out(front, name, payload, timeout, trace_id): - """Services in a blocking fashion a value-in value-out servicer method.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate( - name, payload, True, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - return next(rendezvous) - - -def future_value_in_value_out(front, name, payload, timeout, trace_id): - """Services a value-in value-out servicer method by returning a Future.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate( - name, payload, True, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - operation_future = _OperationFuture(rendezvous, operation) - operation.context.add_termination_callback( - operation_future.on_operation_termination) - return operation_future - - -def inline_value_in_stream_out(front, name, payload, timeout, trace_id): - """Services a value-in stream-out servicer method.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate( - name, payload, True, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - return _OperationCancellableIterator(rendezvous, operation) - - -def blocking_stream_in_value_out( - front, name, payload_iterator, timeout, trace_id): - """Services in a blocking fashion a stream-in value-out servicer method.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate(name, None, False, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - for payload in payload_iterator: - operation.consumer.consume(payload) - operation.consumer.terminate() - return next(rendezvous) - - -def future_stream_in_value_out( - front, name, payload_iterator, timeout, trace_id, pool): - """Services a stream-in value-out servicer method by returning a Future.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate(name, None, False, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - pool.submit( - callable_util.with_exceptions_logged( - _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), - payload_iterator, operation.consumer, lambda: True, True) - operation_future = _OperationFuture(rendezvous, operation) - operation.context.add_termination_callback( - operation_future.on_operation_termination) - return operation_future - - -def inline_stream_in_stream_out( - front, name, payload_iterator, timeout, trace_id, pool): - """Services a stream-in stream-out servicer method.""" - rendezvous = _control.Rendezvous() - subscription = _rendezvous_subscription(rendezvous) - operation = front.operate(name, None, False, timeout, subscription, trace_id) - operation.context.add_termination_callback(rendezvous.set_outcome) - pool.submit( - callable_util.with_exceptions_logged( - _control.pipe_iterator_to_consumer, _ITERATOR_EXCEPTION_LOG_MESSAGE), - payload_iterator, operation.consumer, lambda: True, True) - return _OperationCancellableIterator(rendezvous, operation) - - -def event_value_in_value_out( - front, name, payload, completion_callback, abortion_callback, timeout, - trace_id): - subscription = _unary_event_subscription( - completion_callback, abortion_callback) - operation = front.operate( - name, payload, True, timeout, subscription, trace_id) - return _Call(operation) - - -def event_value_in_stream_out( - front, name, payload, result_payload_consumer, abortion_callback, timeout, - trace_id): - subscription = _stream_event_subscription( - result_payload_consumer, abortion_callback) - operation = front.operate( - name, payload, True, timeout, subscription, trace_id) - return _Call(operation) - - -def event_stream_in_value_out( - front, name, completion_callback, abortion_callback, timeout, trace_id): - subscription = _unary_event_subscription( - completion_callback, abortion_callback) - operation = front.operate(name, None, False, timeout, subscription, trace_id) - return _Call(operation), operation.consumer - - -def event_stream_in_stream_out( - front, name, result_payload_consumer, abortion_callback, timeout, trace_id): - subscription = _stream_event_subscription( - result_payload_consumer, abortion_callback) - operation = front.operate(name, None, False, timeout, subscription, trace_id) - return _Call(operation), operation.consumer diff --git a/src/python/src/grpc/framework/face/_control.py b/src/python/src/grpc/framework/face/_control.py deleted file mode 100644 index e918907b74..0000000000 --- a/src/python/src/grpc/framework/face/_control.py +++ /dev/null @@ -1,198 +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. - -"""State and behavior for translating between sync and async control flow.""" - -import threading - -from grpc.framework.base import interfaces as base_interfaces -from grpc.framework.face import exceptions -from grpc.framework.face import interfaces -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import stream - -INTERNAL_ERROR_LOG_MESSAGE = ':-( RPC Framework (Face) Internal Error! :-(' - -_OPERATION_OUTCOME_TO_RPC_ABORTION = { - base_interfaces.Outcome.CANCELLED: interfaces.Abortion.CANCELLED, - base_interfaces.Outcome.EXPIRED: interfaces.Abortion.EXPIRED, - base_interfaces.Outcome.RECEPTION_FAILURE: - interfaces.Abortion.NETWORK_FAILURE, - base_interfaces.Outcome.TRANSMISSION_FAILURE: - interfaces.Abortion.NETWORK_FAILURE, - base_interfaces.Outcome.SERVICED_FAILURE: - interfaces.Abortion.SERVICED_FAILURE, - base_interfaces.Outcome.SERVICER_FAILURE: - interfaces.Abortion.SERVICER_FAILURE, -} - - -def _as_operation_termination_callback(rpc_abortion_callback): - def operation_termination_callback(operation_outcome): - rpc_abortion = _OPERATION_OUTCOME_TO_RPC_ABORTION.get( - operation_outcome, None) - if rpc_abortion is not None: - rpc_abortion_callback(rpc_abortion) - return operation_termination_callback - - -def _abortion_outcome_to_exception(abortion_outcome): - if abortion_outcome == base_interfaces.Outcome.CANCELLED: - return exceptions.CancellationError() - elif abortion_outcome == base_interfaces.Outcome.EXPIRED: - return exceptions.ExpirationError() - elif abortion_outcome == base_interfaces.Outcome.SERVICER_FAILURE: - return exceptions.ServicerError() - elif abortion_outcome == base_interfaces.Outcome.SERVICED_FAILURE: - return exceptions.ServicedError() - else: - return exceptions.NetworkError() - - -class UnaryConsumer(stream.Consumer): - """A stream.Consumer that should only ever be passed one value.""" - - def __init__(self, on_termination): - self._on_termination = on_termination - self._value = None - - def consume(self, value): - self._value = value - - def terminate(self): - self._on_termination(self._value) - - def consume_and_terminate(self, value): - self._on_termination(value) - - -class Rendezvous(stream.Consumer): - """A rendez-vous with stream.Consumer and iterator interfaces.""" - - def __init__(self): - self._condition = threading.Condition() - self._values = [] - self._values_completed = False - self._abortion = None - - def consume(self, value): - with self._condition: - self._values.append(value) - self._condition.notify() - - def terminate(self): - with self._condition: - self._values_completed = True - self._condition.notify() - - def consume_and_terminate(self, value): - with self._condition: - self._values.append(value) - self._values_completed = True - self._condition.notify() - - def __iter__(self): - return self - - def next(self): - with self._condition: - while ((self._abortion is None) and - (not self._values) and - (not self._values_completed)): - self._condition.wait() - if self._abortion is not None: - raise _abortion_outcome_to_exception(self._abortion) - elif self._values: - return self._values.pop(0) - elif self._values_completed: - raise StopIteration() - else: - raise AssertionError('Unreachable code reached!') - - def set_outcome(self, outcome): - with self._condition: - if outcome is not base_interfaces.Outcome.COMPLETED: - self._abortion = outcome - self._condition.notify() - - -class RpcContext(interfaces.RpcContext): - """A wrapped base_interfaces.OperationContext.""" - - def __init__(self, operation_context): - self._operation_context = operation_context - - def is_active(self): - return self._operation_context.is_active() - - def time_remaining(self): - return self._operation_context.time_remaining() - - def add_abortion_callback(self, abortion_callback): - self._operation_context.add_termination_callback( - _as_operation_termination_callback(abortion_callback)) - - -def pipe_iterator_to_consumer(iterator, consumer, active, terminate): - """Pipes values emitted from an iterator to a stream.Consumer. - - Args: - iterator: An iterator from which values will be emitted. - consumer: A stream.Consumer to which values will be passed. - active: A no-argument callable that returns True if the work being done by - this function is still valid and should not be abandoned and False if the - work being done by this function should be abandoned. - terminate: A boolean indicating whether or not this function should - terminate the given consumer after passing to it all values emitted by the - given iterator. - - Raises: - abandonment.Abandoned: If this function quits early after seeing False - returned by the active function passed to it. - Exception: This function raises whatever exceptions are raised by iterating - over the given iterator. - """ - for element in iterator: - if not active(): - raise abandonment.Abandoned() - - consumer.consume(element) - - if not active(): - raise abandonment.Abandoned() - if terminate: - consumer.terminate() - - -def abortion_outcome_to_exception(abortion_outcome): - return _abortion_outcome_to_exception(abortion_outcome) - - -def as_operation_termination_callback(rpc_abortion_callback): - return _as_operation_termination_callback(rpc_abortion_callback) diff --git a/src/python/src/grpc/framework/face/_service.py b/src/python/src/grpc/framework/face/_service.py deleted file mode 100644 index cdf413356a..0000000000 --- a/src/python/src/grpc/framework/face/_service.py +++ /dev/null @@ -1,187 +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. - -"""Behaviors for servicing RPCs.""" - -# base_interfaces and interfaces are referenced from specification in this -# module. -from grpc.framework.base import interfaces as base_interfaces # pylint: disable=unused-import -from grpc.framework.face import _control -from grpc.framework.face import exceptions -from grpc.framework.face import interfaces # pylint: disable=unused-import -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import callable_util -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util - - -class _ValueInStreamOutConsumer(stream.Consumer): - """A stream.Consumer that maps inputs one-to-many onto outputs.""" - - def __init__(self, behavior, context, downstream): - """Constructor. - - Args: - behavior: A callable that takes a single value and an - interfaces.RpcContext and returns a generator of arbitrarily many - values. - context: An interfaces.RpcContext. - downstream: A stream.Consumer to which to pass the values generated by the - given behavior. - """ - self._behavior = behavior - self._context = context - self._downstream = downstream - - def consume(self, value): - _control.pipe_iterator_to_consumer( - self._behavior(value, self._context), self._downstream, - self._context.is_active, False) - - def terminate(self): - self._downstream.terminate() - - def consume_and_terminate(self, value): - _control.pipe_iterator_to_consumer( - self._behavior(value, self._context), self._downstream, - self._context.is_active, True) - - -def _pool_wrap(behavior, operation_context): - """Wraps an operation-related behavior so that it may be called in a pool. - - Args: - behavior: A callable related to carrying out an operation. - operation_context: A base_interfaces.OperationContext for the operation. - - Returns: - A callable that when called carries out the behavior of the given callable - and handles whatever exceptions it raises appropriately. - """ - def translation(*args): - try: - behavior(*args) - except ( - abandonment.Abandoned, - exceptions.ExpirationError, - exceptions.CancellationError, - exceptions.ServicedError, - exceptions.NetworkError) as e: - if operation_context.is_active(): - operation_context.fail(e) - except Exception as e: - operation_context.fail(e) - return callable_util.with_exceptions_logged( - translation, _control.INTERNAL_ERROR_LOG_MESSAGE) - - -def adapt_inline_value_in_value_out(method): - def adaptation(response_consumer, operation_context): - rpc_context = _control.RpcContext(operation_context) - return stream_util.TransformingConsumer( - lambda request: method(request, rpc_context), response_consumer) - return adaptation - - -def adapt_inline_value_in_stream_out(method): - def adaptation(response_consumer, operation_context): - rpc_context = _control.RpcContext(operation_context) - return _ValueInStreamOutConsumer(method, rpc_context, response_consumer) - return adaptation - - -def adapt_inline_stream_in_value_out(method, pool): - def adaptation(response_consumer, operation_context): - rendezvous = _control.Rendezvous() - operation_context.add_termination_callback(rendezvous.set_outcome) - def in_pool_thread(): - response_consumer.consume_and_terminate( - method(rendezvous, _control.RpcContext(operation_context))) - pool.submit(_pool_wrap(in_pool_thread, operation_context)) - return rendezvous - return adaptation - - -def adapt_inline_stream_in_stream_out(method, pool): - """Adapts an interfaces.InlineStreamInStreamOutMethod for use with Consumers. - - RPCs may be serviced by calling the return value of this function, passing - request values to the stream.Consumer returned from that call, and receiving - response values from the stream.Consumer passed to that call. - - Args: - method: An interfaces.InlineStreamInStreamOutMethod. - pool: A thread pool. - - Returns: - A callable that takes a stream.Consumer and a - base_interfaces.OperationContext and returns a stream.Consumer. - """ - def adaptation(response_consumer, operation_context): - rendezvous = _control.Rendezvous() - operation_context.add_termination_callback(rendezvous.set_outcome) - def in_pool_thread(): - _control.pipe_iterator_to_consumer( - method(rendezvous, _control.RpcContext(operation_context)), - response_consumer, operation_context.is_active, True) - pool.submit(_pool_wrap(in_pool_thread, operation_context)) - return rendezvous - return adaptation - - -def adapt_event_value_in_value_out(method): - def adaptation(response_consumer, operation_context): - def on_payload(payload): - method( - payload, response_consumer.consume_and_terminate, - _control.RpcContext(operation_context)) - return _control.UnaryConsumer(on_payload) - return adaptation - - -def adapt_event_value_in_stream_out(method): - def adaptation(response_consumer, operation_context): - def on_payload(payload): - method( - payload, response_consumer, _control.RpcContext(operation_context)) - return _control.UnaryConsumer(on_payload) - return adaptation - - -def adapt_event_stream_in_value_out(method): - def adaptation(response_consumer, operation_context): - rpc_context = _control.RpcContext(operation_context) - return method(response_consumer.consume_and_terminate, rpc_context) - return adaptation - - -def adapt_event_stream_in_stream_out(method): - def adaptation(response_consumer, operation_context): - return method(response_consumer, _control.RpcContext(operation_context)) - return adaptation diff --git a/src/python/src/grpc/framework/face/_test_case.py b/src/python/src/grpc/framework/face/_test_case.py deleted file mode 100644 index 642d500628..0000000000 --- a/src/python/src/grpc/framework/face/_test_case.py +++ /dev/null @@ -1,61 +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. - -"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" - -from grpc.framework.face import implementations -from grpc.framework.face.testing import base_util -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_POOL_SIZE = 10 - - -class FaceTestCase(test_case.FaceTestCase): - """Provides abstract Face-layer tests an in-memory implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = implementations.servicer( - servicer_pool, method_implementations, multi_method_implementation) - - linked_pair = base_util.linked_pair(servicer, _TIMEOUT) - stub = implementations.generic_stub(linked_pair.front, stub_pool) - return stub, (servicer_pool, stub_pool, linked_pair) - - def tear_down_implementation(self, memo): - servicer_pool, stub_pool, linked_pair = memo - linked_pair.shut_down() - stub_pool.shutdown(wait=True) - servicer_pool.shutdown(wait=True) diff --git a/src/python/src/grpc/framework/face/blocking_invocation_inline_service_test.py b/src/python/src/grpc/framework/face/blocking_invocation_inline_service_test.py deleted file mode 100644 index 763f0f0edc..0000000000 --- a/src/python/src/grpc/framework/face/blocking_invocation_inline_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case - - -class BlockingInvocationInlineServiceTest( - _test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/face/demonstration.py b/src/python/src/grpc/framework/face/demonstration.py deleted file mode 100644 index f6b4b609ff..0000000000 --- a/src/python/src/grpc/framework/face/demonstration.py +++ /dev/null @@ -1,118 +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. - -"""Demonstration-suitable implementation of the face layer of RPC Framework.""" - -from grpc.framework.base import util as _base_util -from grpc.framework.base import implementations as _base_implementations -from grpc.framework.face import implementations -from grpc.framework.foundation import logging_pool - -_POOL_SIZE_LIMIT = 5 - -_MAXIMUM_TIMEOUT = 90 - - -class LinkedPair(object): - """A Server and Stub that are linked to one another. - - Attributes: - server: A Server. - stub: A Stub. - """ - - def shut_down(self): - """Shuts down this object and releases its resources.""" - raise NotImplementedError() - - -class _LinkedPair(LinkedPair): - - def __init__(self, server, stub, front, back, pools): - self.server = server - self.stub = stub - self._front = front - self._back = back - self._pools = pools - - def shut_down(self): - _base_util.wait_for_idle(self._front) - _base_util.wait_for_idle(self._back) - - for pool in self._pools: - pool.shutdown(wait=True) - - -def server_and_stub( - default_timeout, - inline_value_in_value_out_methods=None, - inline_value_in_stream_out_methods=None, - inline_stream_in_value_out_methods=None, - inline_stream_in_stream_out_methods=None, - event_value_in_value_out_methods=None, - event_value_in_stream_out_methods=None, - event_stream_in_value_out_methods=None, - event_stream_in_stream_out_methods=None, - multi_method=None): - """Creates a Server and Stub linked together for use.""" - front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - stub_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - pools = ( - front_work_pool, front_transmission_pool, front_utility_pool, - back_work_pool, back_transmission_pool, back_utility_pool, - stub_pool) - - servicer = implementations.servicer( - back_work_pool, - inline_value_in_value_out_methods=inline_value_in_value_out_methods, - inline_value_in_stream_out_methods=inline_value_in_stream_out_methods, - inline_stream_in_value_out_methods=inline_stream_in_value_out_methods, - inline_stream_in_stream_out_methods=inline_stream_in_stream_out_methods, - event_value_in_value_out_methods=event_value_in_value_out_methods, - event_value_in_stream_out_methods=event_value_in_stream_out_methods, - event_stream_in_value_out_methods=event_stream_in_value_out_methods, - event_stream_in_stream_out_methods=event_stream_in_stream_out_methods, - multi_method=multi_method) - - front = _base_implementations.front_link( - front_work_pool, front_transmission_pool, front_utility_pool) - back = _base_implementations.back_link( - servicer, back_work_pool, back_transmission_pool, back_utility_pool, - default_timeout, _MAXIMUM_TIMEOUT) - front.join_rear_link(back) - back.join_fore_link(front) - - stub = implementations.stub(front, stub_pool) - - return _LinkedPair(implementations.server(), stub, front, back, pools) diff --git a/src/python/src/grpc/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/src/grpc/framework/face/event_invocation_synchronous_event_service_test.py deleted file mode 100644 index e1ab3cf711..0000000000 --- a/src/python/src/grpc/framework/face/event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/face/exceptions.py b/src/python/src/grpc/framework/face/exceptions.py deleted file mode 100644 index f112df70bc..0000000000 --- a/src/python/src/grpc/framework/face/exceptions.py +++ /dev/null @@ -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. - -"""Exceptions used in the Face layer of RPC Framework.""" - -import abc - - -class NoSuchMethodError(Exception): - """Raised by customer code to indicate an unrecognized RPC method name. - - Attributes: - name: The unrecognized name. - """ - - def __init__(self, name): - """Constructor. - - Args: - name: The unrecognized RPC method name. - """ - super(NoSuchMethodError, self).__init__() - self.name = name - - -class RpcError(Exception): - """Common super type for all exceptions raised by the Face layer. - - Only RPC Framework should instantiate and raise these exceptions. - """ - __metaclass__ = abc.ABCMeta - - -class CancellationError(RpcError): - """Indicates that an RPC has been cancelled.""" - - -class ExpirationError(RpcError): - """Indicates that an RPC has expired ("timed out").""" - - -class NetworkError(RpcError): - """Indicates that some error occurred on the network.""" - - -class ServicedError(RpcError): - """Indicates that the Serviced failed in the course of an RPC.""" - - -class ServicerError(RpcError): - """Indicates that the Servicer failed in the course of servicing an RPC.""" diff --git a/src/python/src/grpc/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/src/grpc/framework/face/future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index 2d13bb911d..0000000000 --- a/src/python/src/grpc/framework/face/future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/face/implementations.py b/src/python/src/grpc/framework/face/implementations.py deleted file mode 100644 index 4a6de52974..0000000000 --- a/src/python/src/grpc/framework/face/implementations.py +++ /dev/null @@ -1,318 +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. - -"""Entry points into the Face layer of RPC Framework.""" - -from grpc.framework.common import cardinality -from grpc.framework.common import style -from grpc.framework.base import exceptions as _base_exceptions -from grpc.framework.base import interfaces as base_interfaces -from grpc.framework.face import _calls -from grpc.framework.face import _service -from grpc.framework.face import exceptions -from grpc.framework.face import interfaces - - -class _BaseServicer(base_interfaces.Servicer): - - def __init__(self, methods, multi_method): - self._methods = methods - self._multi_method = multi_method - - def service(self, name, context, output_consumer): - method = self._methods.get(name, None) - if method is not None: - return method(output_consumer, context) - elif self._multi_method is not None: - try: - return self._multi_method.service(name, output_consumer, context) - except exceptions.NoSuchMethodError: - raise _base_exceptions.NoSuchMethodError() - else: - raise _base_exceptions.NoSuchMethodError() - - -class _UnaryUnaryMultiCallable(interfaces.UnaryUnaryMultiCallable): - - def __init__(self, front, name): - self._front = front - self._name = name - - def __call__(self, request, timeout): - return _calls.blocking_value_in_value_out( - self._front, self._name, request, timeout, 'unused trace ID') - - def future(self, request, timeout): - return _calls.future_value_in_value_out( - self._front, self._name, request, timeout, 'unused trace ID') - - def event(self, request, response_callback, abortion_callback, timeout): - return _calls.event_value_in_value_out( - self._front, self._name, request, response_callback, abortion_callback, - timeout, 'unused trace ID') - - -class _UnaryStreamMultiCallable(interfaces.UnaryStreamMultiCallable): - - def __init__(self, front, name): - self._front = front - self._name = name - - def __call__(self, request, timeout): - return _calls.inline_value_in_stream_out( - self._front, self._name, request, timeout, 'unused trace ID') - - def event(self, request, response_consumer, abortion_callback, timeout): - return _calls.event_value_in_stream_out( - self._front, self._name, request, response_consumer, abortion_callback, - timeout, 'unused trace ID') - - -class _StreamUnaryMultiCallable(interfaces.StreamUnaryMultiCallable): - - def __init__(self, front, name, pool): - self._front = front - self._name = name - self._pool = pool - - def __call__(self, request_iterator, timeout): - return _calls.blocking_stream_in_value_out( - self._front, self._name, request_iterator, timeout, 'unused trace ID') - - def future(self, request_iterator, timeout): - return _calls.future_stream_in_value_out( - self._front, self._name, request_iterator, timeout, 'unused trace ID', - self._pool) - - def event(self, response_callback, abortion_callback, timeout): - return _calls.event_stream_in_value_out( - self._front, self._name, response_callback, abortion_callback, timeout, - 'unused trace ID') - - -class _StreamStreamMultiCallable(interfaces.StreamStreamMultiCallable): - - def __init__(self, front, name, pool): - self._front = front - self._name = name - self._pool = pool - - def __call__(self, request_iterator, timeout): - return _calls.inline_stream_in_stream_out( - self._front, self._name, request_iterator, timeout, 'unused trace ID', - self._pool) - - def event(self, response_consumer, abortion_callback, timeout): - return _calls.event_stream_in_stream_out( - self._front, self._name, response_consumer, abortion_callback, timeout, - 'unused trace ID') - - -class _GenericStub(interfaces.GenericStub): - """An interfaces.GenericStub implementation.""" - - def __init__(self, front, pool): - self._front = front - self._pool = pool - - def blocking_value_in_value_out(self, name, request, timeout): - return _calls.blocking_value_in_value_out( - self._front, name, request, timeout, 'unused trace ID') - - def future_value_in_value_out(self, name, request, timeout): - return _calls.future_value_in_value_out( - self._front, name, request, timeout, 'unused trace ID') - - def inline_value_in_stream_out(self, name, request, timeout): - return _calls.inline_value_in_stream_out( - self._front, name, request, timeout, 'unused trace ID') - - def blocking_stream_in_value_out(self, name, request_iterator, timeout): - return _calls.blocking_stream_in_value_out( - self._front, name, request_iterator, timeout, 'unused trace ID') - - def future_stream_in_value_out(self, name, request_iterator, timeout): - return _calls.future_stream_in_value_out( - self._front, name, request_iterator, timeout, 'unused trace ID', - self._pool) - - def inline_stream_in_stream_out(self, name, request_iterator, timeout): - return _calls.inline_stream_in_stream_out( - self._front, name, request_iterator, timeout, 'unused trace ID', - self._pool) - - def event_value_in_value_out( - self, name, request, response_callback, abortion_callback, timeout): - return _calls.event_value_in_value_out( - self._front, name, request, response_callback, abortion_callback, - timeout, 'unused trace ID') - - def event_value_in_stream_out( - self, name, request, response_consumer, abortion_callback, timeout): - return _calls.event_value_in_stream_out( - self._front, name, request, response_consumer, abortion_callback, - timeout, 'unused trace ID') - - def event_stream_in_value_out( - self, name, response_callback, abortion_callback, timeout): - return _calls.event_stream_in_value_out( - self._front, name, response_callback, abortion_callback, timeout, - 'unused trace ID') - - def event_stream_in_stream_out( - self, name, response_consumer, abortion_callback, timeout): - return _calls.event_stream_in_stream_out( - self._front, name, response_consumer, abortion_callback, timeout, - 'unused trace ID') - - def unary_unary_multi_callable(self, name): - return _UnaryUnaryMultiCallable(self._front, name) - - def unary_stream_multi_callable(self, name): - return _UnaryStreamMultiCallable(self._front, name) - - def stream_unary_multi_callable(self, name): - return _StreamUnaryMultiCallable(self._front, name, self._pool) - - def stream_stream_multi_callable(self, name): - return _StreamStreamMultiCallable(self._front, name, self._pool) - - -class _DynamicStub(interfaces.DynamicStub): - """An interfaces.DynamicStub implementation.""" - - def __init__(self, cardinalities, front, pool): - self._cardinalities = cardinalities - self._front = front - self._pool = pool - - def __getattr__(self, attr): - method_cardinality = self._cardinalities.get(attr) - if method_cardinality is cardinality.Cardinality.UNARY_UNARY: - return _UnaryUnaryMultiCallable(self._front, attr) - elif method_cardinality is cardinality.Cardinality.UNARY_STREAM: - return _UnaryStreamMultiCallable(self._front, attr) - elif method_cardinality is cardinality.Cardinality.STREAM_UNARY: - return _StreamUnaryMultiCallable(self._front, attr, self._pool) - elif method_cardinality is cardinality.Cardinality.STREAM_STREAM: - return _StreamStreamMultiCallable(self._front, attr, self._pool) - else: - raise AttributeError('_DynamicStub object has no attribute "%s"!' % attr) - - -def _adapt_method_implementations(method_implementations, pool): - adapted_implementations = {} - for name, method_implementation in method_implementations.iteritems(): - if method_implementation.style is style.Service.INLINE: - if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: - adapted_implementations[name] = _service.adapt_inline_value_in_value_out( - method_implementation.unary_unary_inline) - elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: - adapted_implementations[name] = _service.adapt_inline_value_in_stream_out( - method_implementation.unary_stream_inline) - elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: - adapted_implementations[name] = _service.adapt_inline_stream_in_value_out( - method_implementation.stream_unary_inline, pool) - elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: - adapted_implementations[name] = _service.adapt_inline_stream_in_stream_out( - method_implementation.stream_stream_inline, pool) - elif method_implementation.style is style.Service.EVENT: - if method_implementation.cardinality is cardinality.Cardinality.UNARY_UNARY: - adapted_implementations[name] = _service.adapt_event_value_in_value_out( - method_implementation.unary_unary_event) - elif method_implementation.cardinality is cardinality.Cardinality.UNARY_STREAM: - adapted_implementations[name] = _service.adapt_event_value_in_stream_out( - method_implementation.unary_stream_event) - elif method_implementation.cardinality is cardinality.Cardinality.STREAM_UNARY: - adapted_implementations[name] = _service.adapt_event_stream_in_value_out( - method_implementation.stream_unary_event) - elif method_implementation.cardinality is cardinality.Cardinality.STREAM_STREAM: - adapted_implementations[name] = _service.adapt_event_stream_in_stream_out( - method_implementation.stream_stream_event) - return adapted_implementations - - -def servicer(pool, method_implementations, multi_method_implementation): - """Creates a base_interfaces.Servicer. - - It is guaranteed that any passed interfaces.MultiMethodImplementation will - only be called to service an RPC if there is no - interfaces.MethodImplementation for the RPC method in the passed - method_implementations dictionary. - - Args: - pool: A thread pool. - method_implementations: A dictionary from RPC method name to - interfaces.MethodImplementation object to be used to service the named - RPC method. - multi_method_implementation: An interfaces.MultiMethodImplementation to be - used to service any RPCs not serviced by the - interfaces.MethodImplementations given in the method_implementations - dictionary, or None. - - Returns: - A base_interfaces.Servicer that services RPCs via the given implementations. - """ - adapted_implementations = _adapt_method_implementations( - method_implementations, pool) - return _BaseServicer(adapted_implementations, multi_method_implementation) - - -def generic_stub(front, pool): - """Creates an interfaces.GenericStub. - - Args: - front: A base_interfaces.Front. - pool: A futures.ThreadPoolExecutor. - - Returns: - An interfaces.GenericStub that performs RPCs via the given - base_interfaces.Front. - """ - return _GenericStub(front, pool) - - -def dynamic_stub(cardinalities, front, pool, prefix): - """Creates an interfaces.DynamicStub. - - Args: - cardinalities: A dict from RPC method name to cardinality.Cardinality - value identifying the cardinality of every RPC method to be supported by - the created interfaces.DynamicStub. - front: A base_interfaces.Front. - pool: A futures.ThreadPoolExecutor. - prefix: A string to prepend when mapping requested attribute name to RPC - method name during attribute access on the created - interfaces.DynamicStub. - - Returns: - An interfaces.DynamicStub that performs RPCs via the given - base_interfaces.Front. - """ - return _DynamicStub(cardinalities, front, pool) diff --git a/src/python/src/grpc/framework/face/interfaces.py b/src/python/src/grpc/framework/face/interfaces.py deleted file mode 100644 index b7cc4c1169..0000000000 --- a/src/python/src/grpc/framework/face/interfaces.py +++ /dev/null @@ -1,640 +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. - -"""Interfaces for the face layer of RPC Framework.""" - -import abc -import enum - -# cardinality, style, exceptions, abandonment, future, and stream are -# referenced from specification in this module. -from grpc.framework.common import cardinality # pylint: disable=unused-import -from grpc.framework.common import style # pylint: disable=unused-import -from grpc.framework.face import exceptions # pylint: disable=unused-import -from grpc.framework.foundation import abandonment # pylint: disable=unused-import -from grpc.framework.foundation import future # pylint: disable=unused-import -from grpc.framework.foundation import stream # pylint: disable=unused-import - - -@enum.unique -class Abortion(enum.Enum): - """Categories of RPC abortion.""" - CANCELLED = 'cancelled' - EXPIRED = 'expired' - NETWORK_FAILURE = 'network failure' - SERVICED_FAILURE = 'serviced failure' - SERVICER_FAILURE = 'servicer failure' - - -class CancellableIterator(object): - """Implements the Iterator protocol and affords a cancel method.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __iter__(self): - """Returns the self object in accordance with the Iterator protocol.""" - raise NotImplementedError() - - @abc.abstractmethod - def next(self): - """Returns a value or raises StopIteration per the Iterator protocol.""" - raise NotImplementedError() - - @abc.abstractmethod - def cancel(self): - """Requests cancellation of whatever computation underlies this iterator.""" - raise NotImplementedError() - - -class RpcContext(object): - """Provides RPC-related information and action.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def is_active(self): - """Describes whether the RPC is active or has terminated.""" - raise NotImplementedError() - - @abc.abstractmethod - def time_remaining(self): - """Describes the length of allowed time remaining for the RPC. - - Returns: - A nonnegative float indicating the length of allowed time in seconds - remaining for the RPC to complete before it is considered to have timed - out. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_abortion_callback(self, abortion_callback): - """Registers a callback to be called if the RPC is aborted. - - Args: - abortion_callback: A callable to be called and passed an Abortion value - in the event of RPC abortion. - """ - raise NotImplementedError() - - -class Call(object): - """Invocation-side representation of an RPC. - - Attributes: - context: An RpcContext affording information about the RPC. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def cancel(self): - """Requests cancellation of the RPC.""" - raise NotImplementedError() - - -class UnaryUnaryMultiCallable(object): - """Affords invoking a unary-unary RPC in any call style.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request, timeout): - """Synchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - The response value for the RPC. - - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def future(self, request, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future's result value will be the response value of the RPC. - In the event of RPC abortion, the returned Future's exception value - will be an exceptions.RpcError. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event(self, request, response_callback, abortion_callback, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - response_callback: A callback to be called to accept the restponse value - of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A Call object for the RPC. - """ - raise NotImplementedError() - - -class UnaryStreamMultiCallable(object): - """Affords invoking a unary-stream RPC in any call style.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request, timeout): - """Synchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A CancellableIterator that yields the response values of the RPC and - affords RPC cancellation. Drawing response values from the returned - CancellableIterator may raise exceptions.RpcError indicating abortion - of the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event(self, request, response_consumer, abortion_callback, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - response_consumer: A stream.Consumer to be called to accept the restponse - values of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A Call object for the RPC. - """ - raise NotImplementedError() - - -class StreamUnaryMultiCallable(object): - """Affords invoking a stream-unary RPC in any call style.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request_iterator, timeout): - """Synchronously invokes the underlying RPC. - - Args: - request_iterator: An iterator that yields request values for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - The response value for the RPC. - - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def future(self, request_iterator, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request_iterator: An iterator that yields request values for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future's result value will be the response value of the RPC. - In the event of RPC abortion, the returned Future's exception value - will be an exceptions.RpcError. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event(self, response_callback, abortion_callback, timeout): - """Asynchronously invokes the underlying RPC. - - Args: - request: The request value for the RPC. - response_callback: A callback to be called to accept the restponse value - of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A pair of a Call object for the RPC and a stream.Consumer to which the - request values of the RPC should be passed. - """ - raise NotImplementedError() - - -class StreamStreamMultiCallable(object): - """Affords invoking a stream-stream RPC in any call style.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __call__(self, request_iterator, timeout): - """Synchronously invokes the underlying RPC. - - Args: - request_iterator: An iterator that yields request values for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A CancellableIterator that yields the response values of the RPC and - affords RPC cancellation. Drawing response values from the returned - CancellableIterator may raise exceptions.RpcError indicating abortion - of the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event(self, response_consumer, abortion_callback, timeout): - """Asynchronously invokes the underlying RPC. - -l Args: - response_consumer: A stream.Consumer to be called to accept the restponse - values of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A pair of a Call object for the RPC and a stream.Consumer to which the - request values of the RPC should be passed. - """ - raise NotImplementedError() - - -class MethodImplementation(object): - """A sum type that describes an RPC method implementation. - - Attributes: - cardinality: A cardinality.Cardinality value. - style: A style.Service value. - unary_unary_inline: The implementation of the RPC method as a callable - value that takes a request value and an RpcContext object and returns a - response value. Only non-None if cardinality is - cardinality.Cardinality.UNARY_UNARY and style is style.Service.INLINE. - unary_stream_inline: The implementation of the RPC method as a callable - value that takes a request value and an RpcContext object and returns an - iterator of response values. Only non-None if cardinality is - cardinality.Cardinality.UNARY_STREAM and style is style.Service.INLINE. - stream_unary_inline: The implementation of the RPC method as a callable - value that takes an iterator of request values and an RpcContext object - and returns a response value. Only non-None if cardinality is - cardinality.Cardinality.STREAM_UNARY and style is style.Service.INLINE. - stream_stream_inline: The implementation of the RPC method as a callable - value that takes an iterator of request values and an RpcContext object - and returns an iterator of response values. Only non-None if cardinality - is cardinality.Cardinality.STREAM_STREAM and style is - style.Service.INLINE. - unary_unary_event: The implementation of the RPC method as a callable value - that takes a request value, a response callback to which to pass the - response value of the RPC, and an RpcContext. Only non-None if - cardinality is cardinality.Cardinality.UNARY_UNARY and style is - style.Service.EVENT. - unary_stream_event: The implementation of the RPC method as a callable - value that takes a request value, a stream.Consumer to which to pass the - the response values of the RPC, and an RpcContext. Only non-None if - cardinality is cardinality.Cardinality.UNARY_STREAM and style is - style.Service.EVENT. - stream_unary_event: The implementation of the RPC method as a callable - value that takes a response callback to which to pass the response value - of the RPC and an RpcContext and returns a stream.Consumer to which the - request values of the RPC should be passed. Only non-None if cardinality - is cardinality.Cardinality.STREAM_UNARY and style is style.Service.EVENT. - stream_stream_event: The implementation of the RPC method as a callable - value that takes a stream.Consumer to which to pass the response values - of the RPC and an RpcContext and returns a stream.Consumer to which the - request values of the RPC should be passed. Only non-None if cardinality - is cardinality.Cardinality.STREAM_STREAM and style is - style.Service.EVENT. - """ - __metaclass__ = abc.ABCMeta - - -class MultiMethodImplementation(object): - """A general type able to service many RPC methods.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, name, response_consumer, context): - """Services an RPC. - - Args: - name: The RPC method name. - response_consumer: A stream.Consumer to be called to accept the response - values of the RPC. - context: An RpcContext object. - - Returns: - A stream.Consumer with which to accept the request values of the RPC. The - consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing values to this object. Implementations must not assume that this - object will be called to completion of the request stream or even called - at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - exceptions.NoSuchMethodError: If this MultiMethod does not recognize the - given RPC method name and is not able to service the RPC. - """ - raise NotImplementedError() - - -class GenericStub(object): - """Affords RPC methods to callers.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def blocking_value_in_value_out(self, name, request, timeout): - """Invokes a unary-request-unary-response RPC method. - - This method blocks until either returning the response value of the RPC - (in the event of RPC completion) or raising an exception (in the event of - RPC abortion). - - Args: - name: The RPC method name. - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - The response value for the RPC. - - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def future_value_in_value_out(self, name, request, timeout): - """Invokes a unary-request-unary-response RPC method. - - Args: - name: The RPC method name. - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future will return an outcome indicating that the RPC returned - the response value of the RPC. In the event of RPC abortion, the - returned Future will return an outcome indicating that the RPC raised - an exceptions.RpcError. - """ - raise NotImplementedError() - - @abc.abstractmethod - def inline_value_in_stream_out(self, name, request, timeout): - """Invokes a unary-request-stream-response RPC method. - - Args: - name: The RPC method name. - request: The request value for the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A CancellableIterator that yields the response values of the RPC and - affords RPC cancellation. Drawing response values from the returned - CancellableIterator may raise exceptions.RpcError indicating abortion of - the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def blocking_stream_in_value_out(self, name, request_iterator, timeout): - """Invokes a stream-request-unary-response RPC method. - - This method blocks until either returning the response value of the RPC - (in the event of RPC completion) or raising an exception (in the event of - RPC abortion). - - Args: - name: The RPC method name. - request_iterator: An iterator that yields the request values of the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - The response value for the RPC. - - Raises: - exceptions.RpcError: Indicating that the RPC was aborted. - """ - raise NotImplementedError() - - @abc.abstractmethod - def future_stream_in_value_out(self, name, request_iterator, timeout): - """Invokes a stream-request-unary-response RPC method. - - Args: - name: The RPC method name. - request_iterator: An iterator that yields the request values of the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A future.Future representing the RPC. In the event of RPC completion, the - returned Future will return an outcome indicating that the RPC returned - the response value of the RPC. In the event of RPC abortion, the - returned Future will return an outcome indicating that the RPC raised - an exceptions.RpcError. - """ - raise NotImplementedError() - - @abc.abstractmethod - def inline_stream_in_stream_out(self, name, request_iterator, timeout): - """Invokes a stream-request-stream-response RPC method. - - Args: - name: The RPC method name. - request_iterator: An iterator that yields the request values of the RPC. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A CancellableIterator that yields the response values of the RPC and - affords RPC cancellation. Drawing response values from the returned - CancellableIterator may raise exceptions.RpcError indicating abortion of - the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event_value_in_value_out( - self, name, request, response_callback, abortion_callback, timeout): - """Event-driven invocation of a unary-request-unary-response RPC method. - - Args: - name: The RPC method name. - request: The request value for the RPC. - response_callback: A callback to be called to accept the response value - of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A Call object for the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event_value_in_stream_out( - self, name, request, response_consumer, abortion_callback, timeout): - """Event-driven invocation of a unary-request-stream-response RPC method. - - Args: - name: The RPC method name. - request: The request value for the RPC. - response_consumer: A stream.Consumer to be called to accept the response - values of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A Call object for the RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event_stream_in_value_out( - self, name, response_callback, abortion_callback, timeout): - """Event-driven invocation of a unary-request-unary-response RPC method. - - Args: - name: The RPC method name. - response_callback: A callback to be called to accept the response value - of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A pair of a Call object for the RPC and a stream.Consumer to which the - request values of the RPC should be passed. - """ - raise NotImplementedError() - - @abc.abstractmethod - def event_stream_in_stream_out( - self, name, response_consumer, abortion_callback, timeout): - """Event-driven invocation of a unary-request-stream-response RPC method. - - Args: - name: The RPC method name. - response_consumer: A stream.Consumer to be called to accept the response - values of the RPC. - abortion_callback: A callback to be called and passed an Abortion value - in the event of RPC abortion. - timeout: A duration of time in seconds to allow for the RPC. - - Returns: - A pair of a Call object for the RPC and a stream.Consumer to which the - request values of the RPC should be passed. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_unary_multi_callable(self, name): - """Creates a UnaryUnaryMultiCallable for a unary-unary RPC method. - - Args: - name: The RPC method name. - - Returns: - A UnaryUnaryMultiCallable value for the named unary-unary RPC method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_stream_multi_callable(self, name): - """Creates a UnaryStreamMultiCallable for a unary-stream RPC method. - - Args: - name: The RPC method name. - - Returns: - A UnaryStreamMultiCallable value for the name unary-stream RPC method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_unary_multi_callable(self, name): - """Creates a StreamUnaryMultiCallable for a stream-unary RPC method. - - Args: - name: The RPC method name. - - Returns: - A StreamUnaryMultiCallable value for the named stream-unary RPC method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_stream_multi_callable(self, name): - """Creates a StreamStreamMultiCallable for a stream-stream RPC method. - - Args: - name: The RPC method name. - - Returns: - A StreamStreamMultiCallable value for the named stream-stream RPC method. - """ - raise NotImplementedError() - - -class DynamicStub(object): - """A stub with RPC-method-bound multi-callable attributes. - - Instances of this type responsd to attribute access as follows: if the - requested attribute is the name of a unary-unary RPC method, the value of the - attribute will be a UnaryUnaryMultiCallable with which to invoke the RPC - method; if the requested attribute is the name of a unary-stream RPC method, - the value of the attribute will be a UnaryStreamMultiCallable with which to - invoke the RPC method; if the requested attribute is the name of a - stream-unary RPC method, the value of the attribute will be a - StreamUnaryMultiCallable with which to invoke the RPC method; and if the - requested attribute is the name of a stream-stream RPC method, the value of - the attribute will be a StreamStreamMultiCallable with which to invoke the - RPC method. - """ - __metaclass__ = abc.ABCMeta diff --git a/src/python/src/grpc/framework/face/testing/__init__.py b/src/python/src/grpc/framework/face/testing/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/face/testing/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/face/testing/base_util.py b/src/python/src/grpc/framework/face/testing/base_util.py deleted file mode 100644 index 1df1529b27..0000000000 --- a/src/python/src/grpc/framework/face/testing/base_util.py +++ /dev/null @@ -1,102 +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. - -"""Utilities for creating Base-layer objects for use in Face-layer tests.""" - -import abc - -# interfaces is referenced from specification in this module. -from grpc.framework.base import util as _base_util -from grpc.framework.base import implementations -from grpc.framework.base import in_memory -from grpc.framework.base import interfaces # pylint: disable=unused-import -from grpc.framework.foundation import logging_pool - -_POOL_SIZE_LIMIT = 5 - -_MAXIMUM_TIMEOUT = 90 - - -class LinkedPair(object): - """A Front and Back that are linked to one another. - - Attributes: - front: An interfaces.Front. - back: An interfaces.Back. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def shut_down(self): - """Shuts down this object and releases its resources.""" - raise NotImplementedError() - - -class _LinkedPair(LinkedPair): - - def __init__(self, front, back, pools): - self.front = front - self.back = back - self._pools = pools - - def shut_down(self): - _base_util.wait_for_idle(self.front) - _base_util.wait_for_idle(self.back) - - for pool in self._pools: - pool.shutdown(wait=True) - - -def linked_pair(servicer, default_timeout): - """Creates a Server and Stub linked together for use.""" - link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - pools = ( - link_pool, - front_work_pool, front_transmission_pool, front_utility_pool, - back_work_pool, back_transmission_pool, back_utility_pool) - - link = in_memory.Link(link_pool) - front = implementations.front_link( - front_work_pool, front_transmission_pool, front_utility_pool) - back = implementations.back_link( - servicer, back_work_pool, back_transmission_pool, back_utility_pool, - default_timeout, _MAXIMUM_TIMEOUT) - front.join_rear_link(link) - link.join_fore_link(front) - back.join_fore_link(link) - link.join_rear_link(back) - - return _LinkedPair(front, back, pools) diff --git a/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py deleted file mode 100644 index e57ee00104..0000000000 --- a/src/python/src/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py +++ /dev/null @@ -1,222 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -# unittest is referenced from specification in this module. -import abc -import unittest # pylint: disable=unused-import - -from grpc.framework.face import exceptions -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case - -_TIMEOUT = 3 -_LONG_TIMEOUT = 45 - - -class BlockingInvocationInlineServiceTestCase( - test_case.FaceTestCase, coverage.BlockingCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, None) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.inline_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response = self.stub.blocking_value_in_value_out( - name, request, _LONG_TIMEOUT) - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response = self.stub.blocking_stream_in_value_out( - name, iter(requests), _LONG_TIMEOUT) - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response = self.stub.blocking_value_in_value_out( - name, first_request, _TIMEOUT) - - test_messages.verify(first_request, first_response, self) - - second_response = self.stub.blocking_value_in_value_out( - name, second_request, _TIMEOUT) - - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - multi_callable = self.stub.unary_unary_multi_callable(name) - multi_callable(request, _TIMEOUT) - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - multi_callable = self.stub.stream_unary_multi_callable(name) - multi_callable(iter(requests), _TIMEOUT) - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) diff --git a/src/python/src/grpc/framework/face/testing/callback.py b/src/python/src/grpc/framework/face/testing/callback.py deleted file mode 100644 index d0e63c8c56..0000000000 --- a/src/python/src/grpc/framework/face/testing/callback.py +++ /dev/null @@ -1,94 +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. - -"""A utility useful in tests of asynchronous, event-driven interfaces.""" - -import threading - -from grpc.framework.foundation import stream - - -class Callback(stream.Consumer): - """A utility object useful in tests of asynchronous code.""" - - def __init__(self): - self._condition = threading.Condition() - self._unary_response = None - self._streamed_responses = [] - self._completed = False - self._abortion = None - - def abort(self, abortion): - with self._condition: - self._abortion = abortion - self._condition.notify_all() - - def complete(self, unary_response): - with self._condition: - self._unary_response = unary_response - self._completed = True - self._condition.notify_all() - - def consume(self, streamed_response): - with self._condition: - self._streamed_responses.append(streamed_response) - - def terminate(self): - with self._condition: - self._completed = True - self._condition.notify_all() - - def consume_and_terminate(self, streamed_response): - with self._condition: - self._streamed_responses.append(streamed_response) - self._completed = True - self._condition.notify_all() - - def block_until_terminated(self): - with self._condition: - while self._abortion is None and not self._completed: - self._condition.wait() - - def response(self): - with self._condition: - if self._abortion is None: - return self._unary_response - else: - raise AssertionError('Aborted with abortion "%s"!' % self._abortion) - - def responses(self): - with self._condition: - if self._abortion is None: - return list(self._streamed_responses) - else: - raise AssertionError('Aborted with abortion "%s"!' % self._abortion) - - def abortion(self): - with self._condition: - return self._abortion diff --git a/src/python/src/grpc/framework/face/testing/control.py b/src/python/src/grpc/framework/face/testing/control.py deleted file mode 100644 index 3960c4e649..0000000000 --- a/src/python/src/grpc/framework/face/testing/control.py +++ /dev/null @@ -1,87 +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. - -"""Code for instructing systems under test to block or fail.""" - -import abc -import contextlib -import threading - - -class Control(object): - """An object that accepts program control from a system under test. - - Systems under test passed a Control should call its control() method - frequently during execution. The control() method may block, raise an - exception, or do nothing, all according to the enclosing test's desire for - the system under test to simulate hanging, failing, or functioning. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def control(self): - """Potentially does anything.""" - raise NotImplementedError() - - -class PauseFailControl(Control): - """A Control that can be used to pause or fail code under control.""" - - def __init__(self): - self._condition = threading.Condition() - self._paused = False - self._fail = False - - def control(self): - with self._condition: - if self._fail: - raise ValueError() - - while self._paused: - self._condition.wait() - - @contextlib.contextmanager - def pause(self): - """Pauses code under control while controlling code is in context.""" - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): - """Fails code under control while controlling code is in context.""" - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False diff --git a/src/python/src/grpc/framework/face/testing/coverage.py b/src/python/src/grpc/framework/face/testing/coverage.py deleted file mode 100644 index f3aca113fe..0000000000 --- a/src/python/src/grpc/framework/face/testing/coverage.py +++ /dev/null @@ -1,123 +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. - -"""Governs coverage for the tests of the Face layer of RPC Framework.""" - -import abc - -# These classes are only valid when inherited by unittest.TestCases. -# pylint: disable=invalid-name - - -class BlockingCoverage(object): - """Specification of test coverage for blocking behaviors.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testSuccessfulUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSequentialInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() - - -class FullCoverage(BlockingCoverage): - """Specification of test coverage for non-blocking behaviors.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/face/testing/digest.py b/src/python/src/grpc/framework/face/testing/digest.py deleted file mode 100644 index db8fcbb018..0000000000 --- a/src/python/src/grpc/framework/face/testing/digest.py +++ /dev/null @@ -1,450 +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. - -"""Code for making a service.TestService more amenable to use in tests.""" - -import collections -import threading - -# testing_control, interfaces, and testing_service are referenced from -# specification in this module. -from grpc.framework.common import cardinality -from grpc.framework.common import style -from grpc.framework.face import exceptions -from grpc.framework.face import interfaces as face_interfaces -from grpc.framework.face.testing import control as testing_control # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import service as testing_service # pylint: disable=unused-import -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util - -_IDENTITY = lambda x: x - - -class TestServiceDigest( - collections.namedtuple( - 'TestServiceDigest', - ['name', - 'methods', - 'inline_method_implementations', - 'event_method_implementations', - 'multi_method_implementation', - 'unary_unary_messages_sequences', - 'unary_stream_messages_sequences', - 'stream_unary_messages_sequences', - 'stream_stream_messages_sequences'])): - """A transformation of a service.TestService. - - Attributes: - name: The RPC service name to be used in the test. - methods: A sequence of interfaces.Method objects describing the RPC - methods that will be called during the test. - inline_method_implementations: A dict from RPC method name to - face_interfaces.MethodImplementation object to be used in tests of - in-line calls to behaviors under test. - event_method_implementations: A dict from RPC method name to - face_interfaces.MethodImplementation object to be used in tests of - event-driven calls to behaviors under test. - multi_method_implementation: A face_interfaces.MultiMethodImplementation to - be used in tests of generic calls to behaviors under test. - unary_unary_messages_sequences: A dict from method name to sequence of - service.UnaryUnaryTestMessages objects to be used to test the method - with the given name. - unary_stream_messages_sequences: A dict from method name to sequence of - service.UnaryStreamTestMessages objects to be used to test the method - with the given name. - stream_unary_messages_sequences: A dict from method name to sequence of - service.StreamUnaryTestMessages objects to be used to test the method - with the given name. - stream_stream_messages_sequences: A dict from method name to sequence of - service.StreamStreamTestMessages objects to be used to test the - method with the given name. - serialization: A serial.Serialization object describing serialization - behaviors for all the RPC methods. - """ - - -class _BufferingConsumer(stream.Consumer): - """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" - - def __init__(self): - self.consumed = [] - self.terminated = False - - def consume(self, value): - self.consumed.append(value) - - def terminate(self): - self.terminated = True - - def consume_and_terminate(self, value): - self.consumed.append(value) - self.terminated = True - - -class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_unary_test_method, control): - self._test_method = unary_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.INLINE - - def unary_unary_inline(self, request, context): - response_list = [] - self._test_method.service( - request, response_list.append, context, self._control) - return response_list.pop(0) - - -class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_unary_test_method, control, pool): - self._test_method = unary_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.EVENT - - def unary_unary_event(self, request, response_callback, context): - if self._pool is None: - self._test_method.service( - request, response_callback, context, self._control) - else: - self._pool.submit( - self._test_method.service, request, response_callback, context, - self._control) - - -class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_stream_test_method, control): - self._test_method = unary_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.INLINE - - def unary_stream_inline(self, request, context): - response_consumer = _BufferingConsumer() - self._test_method.service( - request, response_consumer, context, self._control) - for response in response_consumer.consumed: - yield response - - -class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_stream_test_method, control, pool): - self._test_method = unary_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.EVENT - - def unary_stream_event(self, request, response_consumer, context): - if self._pool is None: - self._test_method.service( - request, response_consumer, context, self._control) - else: - self._pool.submit( - self._test_method.service, request, response_consumer, context, - self._control) - - -class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_unary_test_method, control): - self._test_method = stream_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.INLINE - - def stream_unary_inline(self, request_iterator, context): - response_list = [] - request_consumer = self._test_method.service( - response_list.append, context, self._control) - for request in request_iterator: - request_consumer.consume(request) - request_consumer.terminate() - return response_list.pop(0) - - -class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_unary_test_method, control, pool): - self._test_method = stream_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.EVENT - - def stream_unary_event(self, response_callback, context): - request_consumer = self._test_method.service( - response_callback, context, self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_stream_test_method, control): - self._test_method = stream_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.INLINE - - def stream_stream_inline(self, request_iterator, context): - response_consumer = _BufferingConsumer() - request_consumer = self._test_method.service( - response_consumer, context, self._control) - - for request in request_iterator: - request_consumer.consume(request) - while response_consumer.consumed: - yield response_consumer.consumed.pop(0) - response_consumer.terminate() - - -class _EventStreamStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_stream_test_method, control, pool): - self._test_method = stream_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.EVENT - - def stream_stream_event(self, response_consumer, context): - request_consumer = self._test_method.service( - response_consumer, context, self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _UnaryConsumer(stream.Consumer): - """A Consumer that only allows consumption of exactly one value.""" - - def __init__(self, action): - self._lock = threading.Lock() - self._action = action - self._consumed = False - self._terminated = False - - def consume(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - - self._action(value) - - def terminate(self): - with self._lock: - if not self._consumed: - raise ValueError('Unary consumer hasn\'t yet consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._terminated = True - - def consume_and_terminate(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - self._terminated = True - - self._action(value) - - -class _UnaryUnaryAdaptation(object): - - def __init__(self, unary_unary_test_method): - self._method = unary_unary_test_method - - def service(self, response_consumer, context, control): - def action(request): - self._method.service( - request, response_consumer.consume_and_terminate, context, control) - return _UnaryConsumer(action) - - -class _UnaryStreamAdaptation(object): - - def __init__(self, unary_stream_test_method): - self._method = unary_stream_test_method - - def service(self, response_consumer, context, control): - def action(request): - self._method.service(request, response_consumer, context, control) - return _UnaryConsumer(action) - - -class _StreamUnaryAdaptation(object): - - def __init__(self, stream_unary_test_method): - self._method = stream_unary_test_method - - def service(self, response_consumer, context, control): - return self._method.service( - response_consumer.consume_and_terminate, context, control) - - -class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): - - def __init__(self, methods, control, pool): - self._methods = methods - self._control = control - self._pool = pool - - def service(self, name, response_consumer, context): - method = self._methods.get(name, None) - if method is None: - raise exceptions.NoSuchMethodError(name) - elif self._pool is None: - return method(response_consumer, context, self._control) - else: - request_consumer = method(response_consumer, context, self._control) - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _Assembly( - collections.namedtuple( - '_Assembly', - ['methods', 'inlines', 'events', 'adaptations', 'messages'])): - """An intermediate structure created when creating a TestServiceDigest.""" - - -def _assemble( - scenarios, names, inline_method_constructor, event_method_constructor, - adapter, control, pool): - """Creates an _Assembly from the given scenarios.""" - methods = [] - inlines = {} - events = {} - adaptations = {} - messages = {} - for name, scenario in scenarios.iteritems(): - if name in names: - raise ValueError('Repeated name "%s"!' % name) - - test_method = scenario[0] - inline_method = inline_method_constructor(test_method, control) - event_method = event_method_constructor(test_method, control, pool) - adaptation = adapter(test_method) - - methods.append(test_method) - inlines[name] = inline_method - events[name] = event_method - adaptations[name] = adaptation - messages[name] = scenario[1] - - return _Assembly(methods, inlines, events, adaptations, messages) - - -def digest(service, control, pool): - """Creates a TestServiceDigest from a TestService. - - Args: - service: A testing_service.TestService. - control: A testing_control.Control. - pool: If RPC methods should be serviced in a separate thread, a thread pool. - None if RPC methods should be serviced in the thread belonging to the - run-time that calls for their service. - - Returns: - A TestServiceDigest synthesized from the given service.TestService. - """ - names = set() - - unary_unary = _assemble( - service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, - _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) - names.update(set(unary_unary.inlines)) - - unary_stream = _assemble( - service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, - _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) - names.update(set(unary_stream.inlines)) - - stream_unary = _assemble( - service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, - _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) - names.update(set(stream_unary.inlines)) - - stream_stream = _assemble( - service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, - _EventStreamStreamMethod, _IDENTITY, control, pool) - names.update(set(stream_stream.inlines)) - - methods = list(unary_unary.methods) - methods.extend(unary_stream.methods) - methods.extend(stream_unary.methods) - methods.extend(stream_stream.methods) - adaptations = dict(unary_unary.adaptations) - adaptations.update(unary_stream.adaptations) - adaptations.update(stream_unary.adaptations) - adaptations.update(stream_stream.adaptations) - inlines = dict(unary_unary.inlines) - inlines.update(unary_stream.inlines) - inlines.update(stream_unary.inlines) - inlines.update(stream_stream.inlines) - events = dict(unary_unary.events) - events.update(unary_stream.events) - events.update(stream_unary.events) - events.update(stream_stream.events) - - return TestServiceDigest( - service.name(), - methods, - inlines, - events, - _MultiMethodImplementation(adaptations, control, pool), - unary_unary.messages, - unary_stream.messages, - stream_unary.messages, - stream_stream.messages) diff --git a/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py deleted file mode 100644 index 0f0b0e3d52..0000000000 --- a/src/python/src/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py +++ /dev/null @@ -1,362 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -import abc -import unittest - -from grpc.framework.face import interfaces -from grpc.framework.face.testing import callback as testing_callback -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case - -_TIMEOUT = 3 - - -class EventInvocationSynchronousEventServiceTestCase( - test_case.FaceTestCase, coverage.FullCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, None) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.event_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - response = callback.response() - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - responses = callback.responses() - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - response = callback.response() - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - responses = callback.responses() - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - # pylint: disable=cell-var-from-loop - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - first_callback = testing_callback.Callback() - second_callback = testing_callback.Callback() - - def make_second_invocation(first_response): - first_callback.complete(first_response) - self.stub.event_value_in_value_out( - name, second_request, second_callback.complete, - second_callback.abort, _TIMEOUT) - - self.stub.event_value_in_value_out( - name, first_request, make_second_invocation, first_callback.abort, - _TIMEOUT) - second_callback.block_until_terminated() - - first_response = first_callback.response() - second_response = second_callback.response() - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for unused_test_messages in test_messages_sequence: - callback = testing_callback.Callback() - - self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.fail(): - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.fail(): - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - with self.control.fail(): - unused_call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - with self.control.fail(): - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testParallelInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - first_callback = testing_callback.Callback() - second_request = test_messages.request() - second_callback = testing_callback.Callback() - - self.stub.event_value_in_value_out( - name, first_request, first_callback.complete, first_callback.abort, - _TIMEOUT) - self.stub.event_value_in_value_out( - name, second_request, second_callback.complete, - second_callback.abort, _TIMEOUT) - first_callback.block_until_terminated() - second_callback.block_until_terminated() - - first_response = first_callback.response() - second_response = second_callback.response() - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - @unittest.skip('TODO(nathaniel): implement.') - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - def testCancelledUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - call = self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - call = self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for unused_test_messages in test_messages_sequence: - callback = testing_callback.Callback() - - call, unused_request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) diff --git a/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py deleted file mode 100644 index 21bf9a4248..0000000000 --- a/src/python/src/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py +++ /dev/null @@ -1,376 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -import abc -import contextlib -import threading -import unittest - -from grpc.framework.face import exceptions -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import future -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_POOL_SIZE = 10 - - -class _PauseableIterator(object): - - def __init__(self, upstream): - self._upstream = upstream - self._condition = threading.Condition() - self._paused = False - - @contextlib.contextmanager - def pause(self): - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - def __iter__(self): - return self - - def next(self): - with self._condition: - while self._paused: - self._condition.wait() - return next(self._upstream) - - -class FutureInvocationAsynchronousEventServiceTestCase( - test_case.FaceTestCase, coverage.FullCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.event_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - self.digest_pool.shutdown(wait=True) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - response = response_future.result() - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_future = self.stub.future_stream_in_value_out( - name, request_iterator, _TIMEOUT) - response = response_future.result() - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, request_iterator, _TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self.stub.future_value_in_value_out( - name, first_request, _TIMEOUT) - first_response = first_response_future.result() - - test_messages.verify(first_request, first_response, self) - - second_response_future = self.stub.future_value_in_value_out( - name, second_request, _TIMEOUT) - second_response = second_response_future.result() - - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - multi_callable = self.stub.unary_unary_multi_callable(name) - response_future = multi_callable.future(request, _TIMEOUT) - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - multi_callable = self.stub.stream_unary_multi_callable(name) - response_future = multi_callable.future(iter(requests), _TIMEOUT) - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(): - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self.control.fail(), self.assertRaises(exceptions.ExpirationError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(): - response_future = self.stub.future_stream_in_value_out( - name, iter(requests), _TIMEOUT) - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self.control.fail(), self.assertRaises(exceptions.ExpirationError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) - - def testParallelInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self.stub.future_value_in_value_out( - name, first_request, _TIMEOUT) - second_response_future = self.stub.future_value_in_value_out( - name, second_request, _TIMEOUT) - first_response = first_response_future.result() - second_response = second_response_future.result() - - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - @unittest.skip('TODO(nathaniel): implement.') - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - def testCancelledUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - cancel_method_return_value = response_future.cancel() - - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - - def testCancelledUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(future.CancelledError): - next(response_iterator) - - def testCancelledStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_future = self.stub.future_stream_in_value_out( - name, iter(requests), _TIMEOUT) - cancel_method_return_value = response_future.cancel() - - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - - def testCancelledStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(future.CancelledError): - next(response_iterator) diff --git a/src/python/src/grpc/framework/face/testing/interfaces.py b/src/python/src/grpc/framework/face/testing/interfaces.py deleted file mode 100644 index 5932dabf1e..0000000000 --- a/src/python/src/grpc/framework/face/testing/interfaces.py +++ /dev/null @@ -1,117 +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. - -"""Interfaces implemented by data sets used in Face-layer tests.""" - -import abc - -# cardinality is referenced from specification in this module. -from grpc.framework.common import cardinality # pylint: disable=unused-import - - -class Method(object): - """An RPC method to be used in tests of RPC implementations.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def name(self): - """Identify the name of the method. - - Returns: - The name of the method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def cardinality(self): - """Identify the cardinality of the method. - - Returns: - A cardinality.Cardinality value describing the streaming semantics of the - method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def request_class(self): - """Identify the class used for the method's request objects. - - Returns: - The class object of the class to which the method's request objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_class(self): - """Identify the class used for the method's response objects. - - Returns: - The class object of the class to which the method's response objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize the given request object. - - Args: - request: A request object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, serialized_request): - """Synthesize a request object from a given bytestring. - - Args: - serialized_request: A bytestring deserializable into a request object - appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize the given response object. - - Args: - response: A response object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, serialized_response): - """Synthesize a response object from a given bytestring. - - Args: - serialized_response: A bytestring deserializable into a response object - appropriate for this method. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/face/testing/serial.py b/src/python/src/grpc/framework/face/testing/serial.py deleted file mode 100644 index 47fc5822de..0000000000 --- a/src/python/src/grpc/framework/face/testing/serial.py +++ /dev/null @@ -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. - -"""Utility for serialization in the context of test RPC services.""" - -import collections - - -class Serialization( - collections.namedtuple( - '_Serialization', - ['request_serializers', - 'request_deserializers', - 'response_serializers', - 'response_deserializers'])): - """An aggregation of serialization behaviors for an RPC service. - - Attributes: - request_serializers: A dict from method name to request object serializer - behavior. - request_deserializers: A dict from method name to request object - deserializer behavior. - response_serializers: A dict from method name to response object serializer - behavior. - response_deserializers: A dict from method name to response object - deserializer behavior. - """ - - -def serialization(methods): - """Creates a Serialization from a sequences of interfaces.Method objects.""" - request_serializers = {} - request_deserializers = {} - response_serializers = {} - response_deserializers = {} - for method in methods: - name = method.name() - request_serializers[name] = method.serialize_request - request_deserializers[name] = method.deserialize_request - response_serializers[name] = method.serialize_response - response_deserializers[name] = method.deserialize_response - return Serialization( - request_serializers, request_deserializers, response_serializers, - response_deserializers) diff --git a/src/python/src/grpc/framework/face/testing/service.py b/src/python/src/grpc/framework/face/testing/service.py deleted file mode 100644 index bf54d41d66..0000000000 --- a/src/python/src/grpc/framework/face/testing/service.py +++ /dev/null @@ -1,337 +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. - -"""Private interfaces implemented by data sets used in Face-layer tests.""" - -import abc - -# interfaces is referenced from specification in this module. -from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces - - -class UnaryUnaryTestMethodImplementation(interfaces.Method): - """A controllable implementation of a unary-unary RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, request, response_callback, context, control): - """Services an RPC that accepts one message and produces one message. - - Args: - request: The single request message for the RPC. - response_callback: A callback to be called to accept the response message - of the RPC. - context: An face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryUnaryTestMessages(object): - """A type for unary-request-unary-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, response, test_case): - """Verifies that the computed response matches the given request. - - Args: - request: A request message. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class UnaryStreamTestMethodImplementation(interfaces.Method): - """A controllable implementation of a unary-stream RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, request, response_consumer, context, control): - """Services an RPC that takes one message and produces a stream of messages. - - Args: - request: The single request message for the RPC. - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryStreamTestMessages(object): - """A type for unary-request-stream-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, responses, test_case): - """Verifies that the computed responses match the given request. - - Args: - request: A request message. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and responses do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamUnaryTestMethodImplementation(interfaces.Method): - """A controllable implementation of a stream-unary RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, response_callback, context, control): - """Services an RPC that takes a stream of messages and produces one message. - - Args: - response_callback: A callback to be called to accept the response message - of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamUnaryTestMessages(object): - """A type for stream-request-unary-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, response, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamStreamTestMethodImplementation(interfaces.Method): - """A controllable implementation of a stream-stream RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, response_consumer, context, control): - """Services an RPC that accepts and produces streams of messages. - - Args: - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamStreamTestMessages(object): - """A type for stream-request-stream-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, responses, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and responses do not match, indicating - that there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class TestService(object): - """A specification of implemented RPC methods to use in tests.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def name(self): - """Identifies the RPC service name used during the test. - - Returns: - The RPC service name to be used for the test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_unary_scenarios(self): - """Affords unary-request-unary-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair - is a UnaryUnaryTestMethodImplementation object and the second element - is a sequence of UnaryUnaryTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_stream_scenarios(self): - """Affords unary-request-stream-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - UnaryStreamTestMethodImplementation object and the second element is a - sequence of UnaryStreamTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_unary_scenarios(self): - """Affords stream-request-unary-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - StreamUnaryTestMethodImplementation object and the second element is a - sequence of StreamUnaryTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_stream_scenarios(self): - """Affords stream-request-stream-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - StreamStreamTestMethodImplementation object and the second element is a - sequence of StreamStreamTestMethodMessages objects. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/face/testing/stock_service.py b/src/python/src/grpc/framework/face/testing/stock_service.py deleted file mode 100644 index 61aaf444a0..0000000000 --- a/src/python/src/grpc/framework/face/testing/stock_service.py +++ /dev/null @@ -1,374 +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. - -"""Examples of Python implementations of the stock.proto Stock service.""" - -from grpc.framework.common import cardinality -from grpc.framework.face.testing import service -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util -from grpc._junkdrawer import stock_pb2 - -SYMBOL_FORMAT = 'test symbol:%03d' -STREAM_LENGTH = 400 - -# A test-appropriate security-pricing function. :-P -_price = lambda symbol_name: float(hash(symbol_name) % 4096) - - -def _get_last_trade_price(stock_request, stock_reply_callback, control, active): - """A unary-request, unary-response test method.""" - control.control() - if active(): - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=_price(stock_request.symbol))) - else: - raise abandonment.Abandoned() - - -def _get_last_trade_price_multiple(stock_reply_consumer, control, active): - """A stream-request, stream-response test method.""" - def stock_reply_for_stock_request(stock_request): - control.control() - if active(): - return stock_pb2.StockReply( - symbol=stock_request.symbol, price=_price(stock_request.symbol)) - else: - raise abandonment.Abandoned() - return stream_util.TransformingConsumer( - stock_reply_for_stock_request, stock_reply_consumer) - - -def _watch_future_trades(stock_request, stock_reply_consumer, control, active): - """A unary-request, stream-response test method.""" - base_price = _price(stock_request.symbol) - for index in range(stock_request.num_trades_to_watch): - control.control() - if active(): - stock_reply_consumer.consume( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=base_price + index)) - else: - raise abandonment.Abandoned() - stock_reply_consumer.terminate() - - -def _get_highest_trade_price(stock_reply_callback, control, active): - """A stream-request, unary-response test method.""" - - class StockRequestConsumer(stream.Consumer): - """Keeps an ongoing record of the most valuable symbol yet consumed.""" - - def __init__(self): - self._symbol = None - self._price = None - - def consume(self, stock_request): - control.control() - if active(): - if self._price is None: - self._symbol = stock_request.symbol - self._price = _price(stock_request.symbol) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - self._symbol = stock_request.symbol - self._price = candidate_price - - def terminate(self): - control.control() - if active(): - if self._symbol is None: - raise ValueError() - else: - stock_reply_callback( - stock_pb2.StockReply(symbol=self._symbol, price=self._price)) - self._symbol = None - self._price = None - - def consume_and_terminate(self, stock_request): - control.control() - if active(): - if self._price is None: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, - price=_price(stock_request.symbol))) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=candidate_price)) - else: - stock_reply_callback( - stock_pb2.StockReply( - symbol=self._symbol, price=self._price)) - - self._symbol = None - self._price = None - - return StockRequestConsumer() - - -class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): - """GetLastTradePrice for use in tests.""" - - def name(self): - return 'GetLastTradePrice' - - def cardinality(self): - return cardinality.Cardinality.UNARY_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_callback, context, control): - _get_last_trade_price( - request, response_callback, control, context.is_active) - - -class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): - - def __init__(self): - self._index = 0 - - def request(self): - symbol = SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest(symbol=symbol) - - def verify(self, request, response, test_case): - test_case.assertEqual(request.symbol, response.symbol) - test_case.assertEqual(_price(request.symbol), response.price) - - -class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): - """GetLastTradePriceMultiple for use in tests.""" - - def name(self): - return 'GetLastTradePriceMultiple' - - def cardinality(self): - return cardinality.Cardinality.STREAM_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_consumer, context, control): - return _get_last_trade_price_multiple( - response_consumer, control, context.is_active) - - -class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): - """Pairs of message streams for use with GetLastTradePriceMultiple.""" - - def __init__(self): - self._index = 0 - - def requests(self): - base_index = self._index - self._index += 1 - return [ - stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) - for index in range(STREAM_LENGTH)] - - def verify(self, requests, responses, test_case): - test_case.assertEqual(len(requests), len(responses)) - for stock_request, stock_reply in zip(requests, responses): - test_case.assertEqual(stock_request.symbol, stock_reply.symbol) - test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) - - -class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): - """WatchFutureTrades for use in tests.""" - - def name(self): - return 'WatchFutureTrades' - - def cardinality(self): - return cardinality.Cardinality.UNARY_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_consumer, context, control): - _watch_future_trades(request, response_consumer, control, context.is_active) - - -class WatchFutureTradesMessages(service.UnaryStreamTestMessages): - """Pairs of a single request message and a sequence of response messages.""" - - def __init__(self): - self._index = 0 - - def request(self): - symbol = SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest( - symbol=symbol, num_trades_to_watch=STREAM_LENGTH) - - def verify(self, request, responses, test_case): - test_case.assertEqual(STREAM_LENGTH, len(responses)) - base_price = _price(request.symbol) - for index, response in enumerate(responses): - test_case.assertEqual(base_price + index, response.price) - - -class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): - """GetHighestTradePrice for use in tests.""" - - def name(self): - return 'GetHighestTradePrice' - - def cardinality(self): - return cardinality.Cardinality.STREAM_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_callback, context, control): - return _get_highest_trade_price( - response_callback, control, context.is_active) - - -class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): - - def requests(self): - return [ - stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) - for index in range(STREAM_LENGTH)] - - def verify(self, requests, response, test_case): - price = None - symbol = None - for stock_request in requests: - current_symbol = stock_request.symbol - current_price = _price(current_symbol) - if price is None or price < current_price: - price = current_price - symbol = current_symbol - test_case.assertEqual(price, response.price) - test_case.assertEqual(symbol, response.symbol) - - -class StockTestService(service.TestService): - """A corpus of test data with one method of each RPC cardinality.""" - - def name(self): - return 'Stock' - - def unary_unary_scenarios(self): - return { - 'GetLastTradePrice': ( - GetLastTradePrice(), [GetLastTradePriceMessages()]), - } - - def unary_stream_scenarios(self): - return { - 'WatchFutureTrades': ( - WatchFutureTrades(), [WatchFutureTradesMessages()]), - } - - def stream_unary_scenarios(self): - return { - 'GetHighestTradePrice': ( - GetHighestTradePrice(), [GetHighestTradePriceMessages()]) - } - - def stream_stream_scenarios(self): - return { - 'GetLastTradePriceMultiple': ( - GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), - } - - -STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/src/grpc/framework/face/testing/test_case.py b/src/python/src/grpc/framework/face/testing/test_case.py deleted file mode 100644 index e60e3d1d40..0000000000 --- a/src/python/src/grpc/framework/face/testing/test_case.py +++ /dev/null @@ -1,80 +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. - -"""Tools for creating tests of implementations of the Face layer.""" - -import abc - -# face_interfaces and interfaces are referenced in specification in this module. -from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces # pylint: disable=unused-import - - -class FaceTestCase(object): - """Describes a test of the Face Layer of RPC Framework. - - Concrete subclasses must also inherit from unittest.TestCase and from at least - one class that defines test methods. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - """Instantiates the Face Layer implementation under test. - - Args: - name: The service name to be used in the test. - methods: A sequence of interfaces.Method objects describing the RPC - methods that will be called during the test. - method_implementations: A dictionary from string RPC method name to - face_interfaces.MethodImplementation object specifying - implementation of an RPC method. - multi_method_implementation: An face_interfaces.MultiMethodImplementation - or None. - - Returns: - A sequence of length two the first element of which is a - face_interfaces.GenericStub (backed by the given method - implementations), and the second element of which is an arbitrary memo - object to be kept and passed to tearDownImplementation at the conclusion - of the test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def tear_down_implementation(self, memo): - """Destroys the Face layer implementation under test. - - Args: - memo: The object from the second position of the return value of - set_up_implementation. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/face/utilities.py b/src/python/src/grpc/framework/face/utilities.py deleted file mode 100644 index a63fe8c60d..0000000000 --- a/src/python/src/grpc/framework/face/utilities.py +++ /dev/null @@ -1,177 +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. - -"""Utilities for RPC framework's face layer.""" - -import collections - -from grpc.framework.common import cardinality -from grpc.framework.common import style -from grpc.framework.face import interfaces -from grpc.framework.foundation import stream - - -class _MethodImplementation( - interfaces.MethodImplementation, - collections.namedtuple( - '_MethodImplementation', - ['cardinality', 'style', 'unary_unary_inline', 'unary_stream_inline', - 'stream_unary_inline', 'stream_stream_inline', 'unary_unary_event', - 'unary_stream_event', 'stream_unary_event', 'stream_stream_event',])): - pass - - -def unary_unary_inline(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a unary-unary RPC method as a callable value - that takes a request value and an interfaces.RpcContext object and - returns a response value. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.UNARY_UNARY, style.Service.INLINE, behavior, - None, None, None, None, None, None, None) - - -def unary_stream_inline(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a unary-stream RPC method as a callable - value that takes a request value and an interfaces.RpcContext object and - returns an iterator of response values. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.UNARY_STREAM, style.Service.INLINE, None, - behavior, None, None, None, None, None, None) - - -def stream_unary_inline(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a stream-unary RPC method as a callable - value that takes an iterator of request values and an - interfaces.RpcContext object and returns a response value. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.STREAM_UNARY, style.Service.INLINE, None, None, - behavior, None, None, None, None, None) - - -def stream_stream_inline(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a stream-stream RPC method as a callable - value that takes an iterator of request values and an - interfaces.RpcContext object and returns an iterator of response values. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.STREAM_STREAM, style.Service.INLINE, None, None, - None, behavior, None, None, None, None) - - -def unary_unary_event(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a unary-unary RPC method as a callable - value that takes a request value, a response callback to which to pass - the response value of the RPC, and an interfaces.RpcContext. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.UNARY_UNARY, style.Service.EVENT, None, None, - None, None, behavior, None, None, None) - - -def unary_stream_event(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a unary-stream RPC method as a callable - value that takes a request value, a stream.Consumer to which to pass the - the response values of the RPC, and an interfaces.RpcContext. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.UNARY_STREAM, style.Service.EVENT, None, None, - None, None, None, behavior, None, None) - - -def stream_unary_event(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a stream-unary RPC method as a callable - value that takes a response callback to which to pass the response value - of the RPC and an interfaces.RpcContext and returns a stream.Consumer to - which the request values of the RPC should be passed. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.STREAM_UNARY, style.Service.EVENT, None, None, - None, None, None, None, behavior, None) - - -def stream_stream_event(behavior): - """Creates an interfaces.MethodImplementation for the given behavior. - - Args: - behavior: The implementation of a stream-stream RPC method as a callable - value that takes a stream.Consumer to which to pass the response values - of the RPC and an interfaces.RpcContext and returns a stream.Consumer to - which the request values of the RPC should be passed. - - Returns: - An interfaces.MethodImplementation derived from the given behavior. - """ - return _MethodImplementation( - cardinality.Cardinality.STREAM_STREAM, style.Service.EVENT, None, None, - None, None, None, None, None, behavior) diff --git a/src/python/src/grpc/framework/foundation/__init__.py b/src/python/src/grpc/framework/foundation/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/foundation/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/foundation/_later_test.py b/src/python/src/grpc/framework/foundation/_later_test.py deleted file mode 100644 index 6c2459e185..0000000000 --- a/src/python/src/grpc/framework/foundation/_later_test.py +++ /dev/null @@ -1,151 +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. - -"""Tests of the later module.""" - -import threading -import time -import unittest - -from grpc.framework.foundation import later - -TICK = 0.1 - - -class LaterTest(unittest.TestCase): - - def test_simple_delay(self): - lock = threading.Lock() - cell = [0] - return_value = object() - - def computation(): - with lock: - cell[0] += 1 - return return_value - computation_future = later.later(TICK * 2, computation) - - self.assertFalse(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - time.sleep(TICK) - self.assertFalse(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - with lock: - self.assertEqual(0, cell[0]) - time.sleep(TICK * 2) - self.assertTrue(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - with lock: - self.assertEqual(1, cell[0]) - self.assertEqual(return_value, computation_future.result()) - - def test_callback(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback = [None] - def computation(): - with lock: - cell[0] += 1 - computation_future = later.later(TICK * 2, computation) - def callback(outcome): - with lock: - callback_called[0] = True - future_passed_to_callback[0] = outcome - computation_future.add_done_callback(callback) - time.sleep(TICK) - with lock: - self.assertFalse(callback_called[0]) - time.sleep(TICK * 2) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].done()) - - callback_called[0] = False - future_passed_to_callback[0] = None - - computation_future.add_done_callback(callback) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].done()) - - def test_cancel(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback = [None] - def computation(): - with lock: - cell[0] += 1 - computation_future = later.later(TICK * 2, computation) - def callback(outcome): - with lock: - callback_called[0] = True - future_passed_to_callback[0] = outcome - computation_future.add_done_callback(callback) - time.sleep(TICK) - with lock: - self.assertFalse(callback_called[0]) - computation_future.cancel() - self.assertTrue(computation_future.cancelled()) - self.assertFalse(computation_future.running()) - self.assertTrue(computation_future.done()) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].cancelled()) - - def test_result(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback_cell = [None] - return_value = object() - - def computation(): - with lock: - cell[0] += 1 - return return_value - computation_future = later.later(TICK * 2, computation) - - def callback(future_passed_to_callback): - with lock: - callback_called[0] = True - future_passed_to_callback_cell[0] = future_passed_to_callback - computation_future.add_done_callback(callback) - returned_value = computation_future.result() - self.assertEqual(return_value, returned_value) - - # The callback may not yet have been called! Sleep a tick. - time.sleep(TICK) - with lock: - self.assertTrue(callback_called[0]) - self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/foundation/_logging_pool_test.py b/src/python/src/grpc/framework/foundation/_logging_pool_test.py deleted file mode 100644 index 452802da6a..0000000000 --- a/src/python/src/grpc/framework/foundation/_logging_pool_test.py +++ /dev/null @@ -1,64 +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. - -"""Tests for grpc.framework.foundation.logging_pool.""" - -import unittest - -from grpc.framework.foundation import logging_pool - -_POOL_SIZE = 16 - - -class LoggingPoolTest(unittest.TestCase): - - def testUpAndDown(self): - pool = logging_pool.pool(_POOL_SIZE) - pool.shutdown(wait=True) - - with logging_pool.pool(_POOL_SIZE) as pool: - self.assertIsNotNone(pool) - - def testTaskExecuted(self): - test_list = [] - - with logging_pool.pool(_POOL_SIZE) as pool: - pool.submit(lambda: test_list.append(object())).result() - - self.assertTrue(test_list) - - def testException(self): - with logging_pool.pool(_POOL_SIZE) as pool: - raised_exception = pool.submit(lambda: 1/0).exception() - - self.assertIsNotNone(raised_exception) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/src/grpc/framework/foundation/_timer_future.py b/src/python/src/grpc/framework/foundation/_timer_future.py deleted file mode 100644 index 2c9996aa9d..0000000000 --- a/src/python/src/grpc/framework/foundation/_timer_future.py +++ /dev/null @@ -1,228 +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. - -"""Affords a Future implementation based on Python's threading.Timer.""" - -import sys -import threading -import time - -from grpc.framework.foundation import future - - -class TimerFuture(future.Future): - """A Future implementation based around Timer objects.""" - - def __init__(self, compute_time, computation): - """Constructor. - - Args: - compute_time: The time after which to begin this future's computation. - computation: The computation to be performed within this Future. - """ - self._lock = threading.Lock() - self._compute_time = compute_time - self._computation = computation - self._timer = None - self._computing = False - self._computed = False - self._cancelled = False - self._result = None - self._exception = None - self._traceback = None - self._waiting = [] - - def _compute(self): - """Performs the computation embedded in this Future. - - Or doesn't, if the time to perform it has not yet arrived. - """ - with self._lock: - time_remaining = self._compute_time - time.time() - if 0 < time_remaining: - self._timer = threading.Timer(time_remaining, self._compute) - self._timer.start() - return - else: - self._computing = True - - try: - return_value = self._computation() - exception = None - traceback = None - except Exception as e: # pylint: disable=broad-except - return_value = None - exception = e - traceback = sys.exc_info()[2] - - with self._lock: - self._computing = False - self._computed = True - self._return_value = return_value - self._exception = exception - self._traceback = traceback - waiting = self._waiting - - for callback in waiting: - callback(self) - - def start(self): - """Starts this Future. - - This must be called exactly once, immediately after construction. - """ - with self._lock: - self._timer = threading.Timer( - self._compute_time - time.time(), self._compute) - self._timer.start() - - def cancel(self): - """See future.Future.cancel for specification.""" - with self._lock: - if self._computing or self._computed: - return False - elif self._cancelled: - return True - else: - self._timer.cancel() - self._cancelled = True - waiting = self._waiting - - for callback in waiting: - try: - callback(self) - except Exception: # pylint: disable=broad-except - pass - - return True - - def cancelled(self): - """See future.Future.cancelled for specification.""" - with self._lock: - return self._cancelled - - def running(self): - """See future.Future.running for specification.""" - with self._lock: - return not self._computed and not self._cancelled - - def done(self): - """See future.Future.done for specification.""" - with self._lock: - return self._computed or self._cancelled - - def result(self, timeout=None): - """See future.Future.result for specification.""" - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - if self._exception is None: - return self._return_value - else: - raise self._exception # pylint: disable=raising-bad-type - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._waiting.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - if self._exception is None: - return self._return_value - else: - raise self._exception # pylint: disable=raising-bad-type - else: - raise future.TimeoutError() - - def exception(self, timeout=None): - """See future.Future.exception for specification.""" - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._exception - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._waiting.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._exception - else: - raise future.TimeoutError() - - def traceback(self, timeout=None): - """See future.Future.traceback for specification.""" - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._traceback - - condition = threading.Condition() - def notify_condition(unused_future): - with condition: - condition.notify() - self._waiting.append(notify_condition) - - with condition: - condition.wait(timeout=timeout) - - with self._lock: - if self._cancelled: - raise future.CancelledError() - elif self._computed: - return self._traceback - else: - raise future.TimeoutError() - - def add_done_callback(self, fn): - """See future.Future.add_done_callback for specification.""" - with self._lock: - if not self._computed and not self._cancelled: - self._waiting.append(fn) - return - - fn(self) diff --git a/src/python/src/grpc/framework/foundation/abandonment.py b/src/python/src/grpc/framework/foundation/abandonment.py deleted file mode 100644 index 960b4d06b4..0000000000 --- a/src/python/src/grpc/framework/foundation/abandonment.py +++ /dev/null @@ -1,38 +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. - -"""Utilities for indicating abandonment of computation.""" - - -class Abandoned(Exception): - """Indicates that some computation is being abandoned. - - Abandoning a computation is different than returning a value or raising - an exception indicating some operational or programming defect. - """ diff --git a/src/python/src/grpc/framework/foundation/activated.py b/src/python/src/grpc/framework/foundation/activated.py deleted file mode 100644 index 426a71c705..0000000000 --- a/src/python/src/grpc/framework/foundation/activated.py +++ /dev/null @@ -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. - -"""Interfaces related to streams of values or objects.""" - -import abc - - -class Activated(object): - """Interface for objects that may be started and stopped. - - Values implementing this type must also implement the context manager - protocol. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def __enter__(self): - """See the context manager protocol for specification.""" - raise NotImplementedError() - - @abc.abstractmethod - def __exit__(self, exc_type, exc_val, exc_tb): - """See the context manager protocol for specification.""" - raise NotImplementedError() - - @abc.abstractmethod - def start(self): - """Activates this object. - - Returns: - A value equal to the value returned by this object's __enter__ method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stop(self): - """Deactivates this object.""" - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/foundation/callable_util.py b/src/python/src/grpc/framework/foundation/callable_util.py deleted file mode 100644 index 32b0751a01..0000000000 --- a/src/python/src/grpc/framework/foundation/callable_util.py +++ /dev/null @@ -1,107 +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. - -"""Utilities for working with callables.""" - -import abc -import collections -import enum -import functools -import logging - - -class Outcome(object): - """A sum type describing the outcome of some call. - - Attributes: - kind: One of Kind.RETURNED or Kind.RAISED respectively indicating that the - call returned a value or raised an exception. - return_value: The value returned by the call. Must be present if kind is - Kind.RETURNED. - exception: The exception raised by the call. Must be present if kind is - Kind.RAISED. - """ - __metaclass__ = abc.ABCMeta - - @enum.unique - class Kind(enum.Enum): - """Identifies the general kind of the outcome of some call.""" - - RETURNED = object() - RAISED = object() - - -class _EasyOutcome( - collections.namedtuple( - '_EasyOutcome', ['kind', 'return_value', 'exception']), - Outcome): - """A trivial implementation of Outcome.""" - - -def _call_logging_exceptions(behavior, message, *args, **kwargs): - try: - return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs), None) - except Exception as e: # pylint: disable=broad-except - logging.exception(message) - return _EasyOutcome(Outcome.Kind.RAISED, None, e) - - -def with_exceptions_logged(behavior, message): - """Wraps a callable in a try-except that logs any exceptions it raises. - - Args: - behavior: Any callable. - message: A string to log if the behavior raises an exception. - - Returns: - A callable that when executed invokes the given behavior. The returned - callable takes the same arguments as the given behavior but returns a - future.Outcome describing whether the given behavior returned a value or - raised an exception. - """ - @functools.wraps(behavior) - def wrapped_behavior(*args, **kwargs): - return _call_logging_exceptions(behavior, message, *args, **kwargs) - return wrapped_behavior - - -def call_logging_exceptions(behavior, message, *args, **kwargs): - """Calls a behavior in a try-except that logs any exceptions it raises. - - Args: - behavior: Any callable. - message: A string to log if the behavior raises an exception. - *args: Positional arguments to pass to the given behavior. - **kwargs: Keyword arguments to pass to the given behavior. - - Returns: - An Outcome describing whether the given behavior returned a value or raised - an exception. - """ - return _call_logging_exceptions(behavior, message, *args, **kwargs) diff --git a/src/python/src/grpc/framework/foundation/future.py b/src/python/src/grpc/framework/foundation/future.py deleted file mode 100644 index bfc16fc1ea..0000000000 --- a/src/python/src/grpc/framework/foundation/future.py +++ /dev/null @@ -1,236 +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. - -"""A Future interface. - -Python doesn't have a Future interface in its standard library. In the absence -of such a standard, three separate, incompatible implementations -(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This -interface attempts to be as compatible as possible with -concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor -method. - -Unlike the concrete and implemented Future classes listed above, the Future -class defined in this module is an entirely abstract interface that anyone may -implement and use. - -The one known incompatibility between this interface and the interface of -concurrent.futures.Future is that this interface defines its own CancelledError -and TimeoutError exceptions rather than raising the implementation-private -concurrent.futures._base.CancelledError and the -built-in-but-only-in-3.3-and-later TimeoutError. -""" - -import abc - - -class TimeoutError(Exception): - """Indicates that a particular call timed out.""" - - -class CancelledError(Exception): - """Indicates that the computation underlying a Future was cancelled.""" - - -class Future(object): - """A representation of a computation in another control flow. - - Computations represented by a Future may be yet to be begun, may be ongoing, - or may have already completed. - """ - __metaclass__ = abc.ABCMeta - - # NOTE(nathaniel): This isn't the return type that I would want to have if it - # were up to me. Were this interface being written from scratch, the return - # type of this method would probably be a sum type like: - # - # NOT_COMMENCED - # COMMENCED_AND_NOT_COMPLETED - # PARTIAL_RESULT - # COMPLETED - # UNCANCELLABLE - # NOT_IMMEDIATELY_DETERMINABLE - @abc.abstractmethod - def cancel(self): - """Attempts to cancel the computation. - - This method does not block. - - Returns: - True if the computation has not yet begun, will not be allowed to take - place, and determination of both was possible without blocking. False - under all other circumstances including but not limited to the - computation's already having begun, the computation's already having - finished, and the computation's having been scheduled for execution on a - remote system for which a determination of whether or not it commenced - before being cancelled cannot be made without blocking. - """ - raise NotImplementedError() - - # NOTE(nathaniel): Here too this isn't the return type that I'd want this - # method to have if it were up to me. I think I'd go with another sum type - # like: - # - # NOT_CANCELLED (this object's cancel method hasn't been called) - # NOT_COMMENCED - # COMMENCED_AND_NOT_COMPLETED - # PARTIAL_RESULT - # COMPLETED - # UNCANCELLABLE - # NOT_IMMEDIATELY_DETERMINABLE - # - # Notice how giving the cancel method the right semantics obviates most - # reasons for this method to exist. - @abc.abstractmethod - def cancelled(self): - """Describes whether the computation was cancelled. - - This method does not block. - - Returns: - True if the computation was cancelled any time before its result became - immediately available. False under all other circumstances including but - not limited to this object's cancel method not having been called and - the computation's result having become immediately available. - """ - raise NotImplementedError() - - @abc.abstractmethod - def running(self): - """Describes whether the computation is taking place. - - This method does not block. - - Returns: - True if the computation is scheduled to take place in the future or is - taking place now, or False if the computation took place in the past or - was cancelled. - """ - raise NotImplementedError() - - # NOTE(nathaniel): These aren't quite the semantics I'd like here either. I - # would rather this only returned True in cases in which the underlying - # computation completed successfully. A computation's having been cancelled - # conflicts with considering that computation "done". - @abc.abstractmethod - def done(self): - """Describes whether the computation has taken place. - - This method does not block. - - Returns: - True if the computation is known to have either completed or have been - unscheduled or interrupted. False if the computation may possibly be - executing or scheduled to execute later. - """ - raise NotImplementedError() - - @abc.abstractmethod - def result(self, timeout=None): - """Accesses the outcome of the computation or raises its exception. - - This method may return immediately or may block. - - Args: - timeout: The length of time in seconds to wait for the computation to - finish or be cancelled, or None if this method should block until the - computation has finished or is cancelled no matter how long that takes. - - Returns: - The return value of the computation. - - Raises: - TimeoutError: If a timeout value is passed and the computation does not - terminate within the allotted time. - CancelledError: If the computation was cancelled. - Exception: If the computation raised an exception, this call will raise - the same exception. - """ - raise NotImplementedError() - - @abc.abstractmethod - def exception(self, timeout=None): - """Return the exception raised by the computation. - - This method may return immediately or may block. - - Args: - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - - Returns: - The exception raised by the computation, or None if the computation did - not raise an exception. - - Raises: - TimeoutError: If a timeout value is passed and the computation does not - terminate within the allotted time. - CancelledError: If the computation was cancelled. - """ - raise NotImplementedError() - - @abc.abstractmethod - def traceback(self, timeout=None): - """Access the traceback of the exception raised by the computation. - - This method may return immediately or may block. - - Args: - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - - Returns: - The traceback of the exception raised by the computation, or None if the - computation did not raise an exception. - - Raises: - TimeoutError: If a timeout value is passed and the computation does not - terminate within the allotted time. - CancelledError: If the computation was cancelled. - """ - raise NotImplementedError() - - @abc.abstractmethod - def add_done_callback(self, fn): - """Adds a function to be called at completion of the computation. - - The callback will be passed this Future object describing the outcome of - the computation. - - If the computation has already completed, the callback will be called - immediately. - - Args: - fn: A callable taking a this Future object as its single parameter. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/foundation/later.py b/src/python/src/grpc/framework/foundation/later.py deleted file mode 100644 index 1d1e065041..0000000000 --- a/src/python/src/grpc/framework/foundation/later.py +++ /dev/null @@ -1,51 +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. - -"""Enables scheduling execution at a later time.""" - -import time - -from grpc.framework.foundation import _timer_future - - -def later(delay, computation): - """Schedules later execution of a callable. - - Args: - delay: Any numeric value. Represents the minimum length of time in seconds - to allow to pass before beginning the computation. No guarantees are made - about the maximum length of time that will pass. - computation: A callable that accepts no arguments. - - Returns: - A Future representing the scheduled computation. - """ - timer_future = _timer_future.TimerFuture(time.time() + delay, computation) - timer_future.start() - return timer_future diff --git a/src/python/src/grpc/framework/foundation/logging_pool.py b/src/python/src/grpc/framework/foundation/logging_pool.py deleted file mode 100644 index 7c7a6eebfc..0000000000 --- a/src/python/src/grpc/framework/foundation/logging_pool.py +++ /dev/null @@ -1,83 +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. - -"""A thread pool that logs exceptions raised by tasks executed within it.""" - -import functools -import logging - -from concurrent import futures - - -def _wrap(behavior): - """Wraps an arbitrary callable behavior in exception-logging.""" - @functools.wraps(behavior) - def _wrapping(*args, **kwargs): - try: - return behavior(*args, **kwargs) - except Exception as e: - logging.exception('Unexpected exception from task run in logging pool!') - raise - return _wrapping - - -class _LoggingPool(object): - """An exception-logging futures.ThreadPoolExecutor-compatible thread pool.""" - - def __init__(self, backing_pool): - self._backing_pool = backing_pool - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self._backing_pool.shutdown(wait=True) - - def submit(self, fn, *args, **kwargs): - return self._backing_pool.submit(_wrap(fn), *args, **kwargs) - - def map(self, func, *iterables, **kwargs): - return self._backing_pool.map( - _wrap(func), *iterables, timeout=kwargs.get('timeout', None)) - - def shutdown(self, wait=True): - self._backing_pool.shutdown(wait=wait) - - -def pool(max_workers): - """Creates a thread pool that logs exceptions raised by the tasks within it. - - Args: - max_workers: The maximum number of worker threads to allow the pool. - - Returns: - A futures.ThreadPoolExecutor-compatible thread pool that logs exceptions - raised by the tasks executed within it. - """ - return _LoggingPool(futures.ThreadPoolExecutor(max_workers)) diff --git a/src/python/src/grpc/framework/foundation/relay.py b/src/python/src/grpc/framework/foundation/relay.py deleted file mode 100644 index 9c23946552..0000000000 --- a/src/python/src/grpc/framework/foundation/relay.py +++ /dev/null @@ -1,175 +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. - -"""Implementations of in-order work deference.""" - -import abc -import enum -import threading - -from grpc.framework.foundation import activated -from grpc.framework.foundation import logging_pool - -_NULL_BEHAVIOR = lambda unused_value: None - - -class Relay(object): - """Performs work submitted to it in another thread. - - Performs work in the order in which work was submitted to it; otherwise there - would be no reason to use an implementation of this interface instead of a - thread pool. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def add_value(self, value): - """Adds a value to be passed to the behavior registered with this Relay. - - Args: - value: A value that will be passed to a call made in another thread to the - behavior registered with this Relay. - """ - raise NotImplementedError() - - @abc.abstractmethod - def set_behavior(self, behavior): - """Sets the behavior that this Relay should call when passed values. - - Args: - behavior: The behavior that this Relay should call in another thread when - passed a value, or None to have passed values ignored. - """ - raise NotImplementedError() - - -class _PoolRelay(activated.Activated, Relay): - - @enum.unique - class _State(enum.Enum): - INACTIVE = 'inactive' - IDLE = 'idle' - SPINNING = 'spinning' - - def __init__(self, pool, behavior): - self._condition = threading.Condition() - self._pool = pool - self._own_pool = pool is None - self._state = _PoolRelay._State.INACTIVE - self._activated = False - self._spinning = False - self._values = [] - self._behavior = _NULL_BEHAVIOR if behavior is None else behavior - - def _spin(self, behavior, value): - while True: - behavior(value) - with self._condition: - if self._values: - value = self._values.pop(0) - behavior = self._behavior - else: - self._state = _PoolRelay._State.IDLE - self._condition.notify_all() - break - - def add_value(self, value): - with self._condition: - if self._state is _PoolRelay._State.INACTIVE: - raise ValueError('add_value not valid on inactive Relay!') - elif self._state is _PoolRelay._State.IDLE: - self._pool.submit(self._spin, self._behavior, value) - self._state = _PoolRelay._State.SPINNING - else: - self._values.append(value) - - def set_behavior(self, behavior): - with self._condition: - self._behavior = _NULL_BEHAVIOR if behavior is None else behavior - - def _start(self): - with self._condition: - self._state = _PoolRelay._State.IDLE - if self._own_pool: - self._pool = logging_pool.pool(1) - return self - - def _stop(self): - with self._condition: - while self._state is _PoolRelay._State.SPINNING: - self._condition.wait() - if self._own_pool: - self._pool.shutdown(wait=True) - self._state = _PoolRelay._State.INACTIVE - - def __enter__(self): - return self._start() - - def __exit__(self, exc_type, exc_val, exc_tb): - self._stop() - return False - - def start(self): - return self._start() - - def stop(self): - self._stop() - - -def relay(behavior): - """Creates a Relay. - - Args: - behavior: The behavior to be called by the created Relay, or None to have - passed values dropped until a different behavior is given to the returned - Relay later. - - Returns: - An object that is both an activated.Activated and a Relay. The object is - only valid for use as a Relay when activated. - """ - return _PoolRelay(None, behavior) - - -def pool_relay(pool, behavior): - """Creates a Relay that uses a given thread pool. - - This object will make use of at most one thread in the given pool. - - Args: - pool: A futures.ThreadPoolExecutor for use by the created Relay. - behavior: The behavior to be called by the created Relay, or None to have - passed values dropped until a different behavior is given to the returned - Relay later. - - Returns: - An object that is both an activated.Activated and a Relay. The object is - only valid for use as a Relay when activated. - """ - return _PoolRelay(pool, behavior) diff --git a/src/python/src/grpc/framework/foundation/stream.py b/src/python/src/grpc/framework/foundation/stream.py deleted file mode 100644 index 75c0cf145b..0000000000 --- a/src/python/src/grpc/framework/foundation/stream.py +++ /dev/null @@ -1,60 +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. - -"""Interfaces related to streams of values or objects.""" - -import abc - - -class Consumer(object): - """Interface for consumers of finite streams of values or objects.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def consume(self, value): - """Accepts a value. - - Args: - value: Any value accepted by this Consumer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def terminate(self): - """Indicates to this Consumer that no more values will be supplied.""" - raise NotImplementedError() - - @abc.abstractmethod - def consume_and_terminate(self, value): - """Supplies a value and signals that no more values will be supplied. - - Args: - value: Any value accepted by this Consumer. - """ - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/foundation/stream_testing.py b/src/python/src/grpc/framework/foundation/stream_testing.py deleted file mode 100644 index 098a53d5e7..0000000000 --- a/src/python/src/grpc/framework/foundation/stream_testing.py +++ /dev/null @@ -1,73 +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. - -"""Utilities for testing stream-related code.""" - -from grpc.framework.foundation import stream - - -class TestConsumer(stream.Consumer): - """A stream.Consumer instrumented for testing. - - Attributes: - calls: A sequence of value-termination pairs describing the history of calls - made on this object. - """ - - def __init__(self): - self.calls = [] - - def consume(self, value): - """See stream.Consumer.consume for specification.""" - self.calls.append((value, False)) - - def terminate(self): - """See stream.Consumer.terminate for specification.""" - self.calls.append((None, True)) - - def consume_and_terminate(self, value): - """See stream.Consumer.consume_and_terminate for specification.""" - self.calls.append((value, True)) - - def is_legal(self): - """Reports whether or not a legal sequence of calls has been made.""" - terminated = False - for value, terminal in self.calls: - if terminated: - return False - elif terminal: - terminated = True - elif value is None: - return False - else: # pylint: disable=useless-else-on-loop - return True - - def values(self): - """Returns the sequence of values that have been passed to this Consumer.""" - return [value for value, _ in self.calls if value] diff --git a/src/python/src/grpc/framework/foundation/stream_util.py b/src/python/src/grpc/framework/foundation/stream_util.py deleted file mode 100644 index 2210e4efcf..0000000000 --- a/src/python/src/grpc/framework/foundation/stream_util.py +++ /dev/null @@ -1,160 +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. - -"""Helpful utilities related to the stream module.""" - -import logging -import threading - -from grpc.framework.foundation import stream - -_NO_VALUE = object() - - -class TransformingConsumer(stream.Consumer): - """A stream.Consumer that passes a transformation of its input to another.""" - - def __init__(self, transformation, downstream): - self._transformation = transformation - self._downstream = downstream - - def consume(self, value): - self._downstream.consume(self._transformation(value)) - - def terminate(self): - self._downstream.terminate() - - def consume_and_terminate(self, value): - self._downstream.consume_and_terminate(self._transformation(value)) - - -class IterableConsumer(stream.Consumer): - """A Consumer that when iterated over emits the values it has consumed.""" - - def __init__(self): - self._condition = threading.Condition() - self._values = [] - self._active = True - - def consume(self, stock_reply): - with self._condition: - if self._active: - self._values.append(stock_reply) - self._condition.notify() - - def terminate(self): - with self._condition: - self._active = False - self._condition.notify() - - def consume_and_terminate(self, stock_reply): - with self._condition: - if self._active: - self._values.append(stock_reply) - self._active = False - self._condition.notify() - - def __iter__(self): - return self - - def next(self): - with self._condition: - while self._active and not self._values: - self._condition.wait() - if self._values: - return self._values.pop(0) - else: - raise StopIteration() - - -class ThreadSwitchingConsumer(stream.Consumer): - """A Consumer decorator that affords serialization and asynchrony.""" - - def __init__(self, sink, pool): - self._lock = threading.Lock() - self._sink = sink - self._pool = pool - # True if self._spin has been submitted to the pool to be called once and - # that call has not yet returned, False otherwise. - self._spinning = False - self._values = [] - self._active = True - - def _spin(self, sink, value, terminate): - while True: - try: - if value is _NO_VALUE: - sink.terminate() - elif terminate: - sink.consume_and_terminate(value) - else: - sink.consume(value) - except Exception as e: # pylint:disable=broad-except - logging.exception(e) - - with self._lock: - if terminate: - self._spinning = False - return - elif self._values: - value = self._values.pop(0) - terminate = not self._values and not self._active - elif not self._active: - value = _NO_VALUE - terminate = True - else: - self._spinning = False - return - - def consume(self, value): - with self._lock: - if self._active: - if self._spinning: - self._values.append(value) - else: - self._pool.submit(self._spin, self._sink, value, False) - self._spinning = True - - def terminate(self): - with self._lock: - if self._active: - self._active = False - if not self._spinning: - self._pool.submit(self._spin, self._sink, _NO_VALUE, True) - self._spinning = True - - def consume_and_terminate(self, value): - with self._lock: - if self._active: - self._active = False - if self._spinning: - self._values.append(value) - else: - self._pool.submit(self._spin, self._sink, value, True) - self._spinning = True diff --git a/src/python/src/grpc/framework/interfaces/__init__.py b/src/python/src/grpc/framework/interfaces/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/interfaces/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/interfaces/links/__init__.py b/src/python/src/grpc/framework/interfaces/links/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/src/grpc/framework/interfaces/links/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/src/grpc/framework/interfaces/links/links.py b/src/python/src/grpc/framework/interfaces/links/links.py deleted file mode 100644 index 5ebbac8a6f..0000000000 --- a/src/python/src/grpc/framework/interfaces/links/links.py +++ /dev/null @@ -1,124 +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. - -"""The low-level ticket-exchanging-links interface of RPC Framework.""" - -import abc -import collections -import enum - - -class Ticket( - collections.namedtuple( - 'Ticket', - ['operation_id', 'sequence_number', 'group', 'method', 'subscription', - 'timeout', 'allowance', 'initial_metadata', 'payload', - 'terminal_metadata', 'code', 'message', 'termination'])): - """A sum type for all values sent from a front to a back. - - Attributes: - operation_id: A unique-with-respect-to-equality hashable object identifying - a particular operation. - sequence_number: A zero-indexed integer sequence number identifying the - ticket's place in the stream of tickets sent in one direction for the - particular operation. - group: The group to which the method of the operation belongs. Must be - present in the first ticket from invocation side to service side. Ignored - for all other tickets exchanged during the operation. - method: The name of an operation. Must be present in the first ticket from - invocation side to service side. Ignored for all other tickets exchanged - during the operation. - subscription: A Subscription value describing the interest one side has in - receiving information from the other side. Must be present in the first - ticket from either side. Ignored for all other tickets exchanged during - the operation. - timeout: A nonzero length of time (measured from the beginning of the - operation) to allow for the entire operation. Must be present in the first - ticket from invocation side to service side. Optional for all other - tickets exchanged during the operation. Receipt of a value from the other - side of the operation indicates the value in use by that side. Setting a - value on a later ticket allows either side to request time extensions (or - even time reductions!) on in-progress operations. - allowance: A positive integer granting permission for a number of payloads - to be transmitted to the communicating side of the operation, or None if - no additional allowance is being granted with this ticket. - initial_metadata: An optional metadata value communicated from one side to - the other at the beginning of the operation. May be non-None in at most - one ticket from each side. Any non-None value must appear no later than - the first payload value. - payload: A customer payload object. May be None. - terminal_metadata: A metadata value comminicated from one side to the other - at the end of the operation. May be non-None in the same ticket as - the code and message, but must be None for all earlier tickets. - code: A value communicated at operation completion. May be None. - message: A value communicated at operation completion. May be None. - termination: A Termination value describing the end of the operation, or - None if the operation has not yet terminated. If set, no further tickets - may be sent in the same direction. - """ - - @enum.unique - class Subscription(enum.Enum): - """Identifies the level of subscription of a side of an operation.""" - - NONE = 'none' - TERMINATION = 'termination' - FULL = 'full' - - @enum.unique - class Termination(enum.Enum): - """Identifies the termination of an operation.""" - - COMPLETION = 'completion' - CANCELLATION = 'cancellation' - EXPIRATION = 'expiration' - LOCAL_SHUTDOWN = 'local shutdown' - RECEPTION_FAILURE = 'reception failure' - TRANSMISSION_FAILURE = 'transmission failure' - LOCAL_FAILURE = 'local failure' - REMOTE_FAILURE = 'remote failure' - - -class Link(object): - """Accepts and emits tickets.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def accept_ticket(self, ticket): - """Accept a Ticket. - - Args: - ticket: Any Ticket. - """ - raise NotImplementedError() - - @abc.abstractmethod - def join_link(self, link): - """Mates this object with a peer with which it will exchange tickets.""" - raise NotImplementedError() diff --git a/src/python/src/grpc/framework/interfaces/links/test_cases.py b/src/python/src/grpc/framework/interfaces/links/test_cases.py deleted file mode 100644 index bf1f09d99d..0000000000 --- a/src/python/src/grpc/framework/interfaces/links/test_cases.py +++ /dev/null @@ -1,333 +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. - -"""Tests of the links interface of RPC Framework.""" - -# unittest is referenced from specification in this module. -import abc -import unittest # pylint: disable=unused-import - -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_utilities - - -def at_least_n_payloads_received_predicate(n): - def predicate(ticket_sequence): - payload_count = 0 - for ticket in ticket_sequence: - if ticket.payload is not None: - payload_count += 1 - if n <= payload_count: - return True - else: - return False - return predicate - - -def terminated(ticket_sequence): - return ticket_sequence and ticket_sequence[-1].termination is not None - -_TRANSMISSION_GROUP = 'test.Group' -_TRANSMISSION_METHOD = 'TestMethod' - - -class TransmissionTest(object): - """Tests ticket transmission between two connected links. - - This class must be mixed into a unittest.TestCase that implements the abstract - methods it provides. - """ - __metaclass__ = abc.ABCMeta - - # This is a unittest.TestCase mix-in. - # pylint: disable=invalid-name - - @abc.abstractmethod - def create_transmitting_links(self): - """Creates two connected links for use in this test. - - Returns: - Two links.Links, the first of which will be used on the invocation side - of RPCs and the second of which will be used on the service side of - RPCs. - """ - raise NotImplementedError() - - @abc.abstractmethod - def destroy_transmitting_links(self, invocation_side_link, service_side_link): - """Destroys the two connected links created for this test. - - - Args: - invocation_side_link: The link used on the invocation side of RPCs in - this test. - service_side_link: The link used on the service side of RPCs in this - test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_initial_metadata(self): - """Creates a value for use as invocation-side initial metadata. - - Returns: - A metadata value appropriate for use as invocation-side initial metadata - or None if invocation-side initial metadata transmission is not - supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_terminal_metadata(self): - """Creates a value for use as invocation-side terminal metadata. - - Returns: - A metadata value appropriate for use as invocation-side terminal - metadata or None if invocation-side terminal metadata transmission is - not supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_initial_metadata(self): - """Creates a value for use as service-side initial metadata. - - Returns: - A metadata value appropriate for use as service-side initial metadata or - None if service-side initial metadata transmission is not supported by - the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_terminal_metadata(self): - """Creates a value for use as service-side terminal metadata. - - Returns: - A metadata value appropriate for use as service-side terminal metadata or - None if service-side terminal metadata transmission is not supported by - the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_completion(self): - """Creates values for use as invocation-side code and message. - - Returns: - An invocation-side code value and an invocation-side message value. - Either or both may be None if invocation-side code and/or - invocation-side message transmission is not supported by the links - under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_completion(self): - """Creates values for use as service-side code and message. - - Returns: - A service-side code value and a service-side message value. Either or - both may be None if service-side code and/or service-side message - transmission is not supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): - """Asserts that transmitted_metadata contains original_metadata. - - Args: - original_metadata: A metadata object used in this test. - transmitted_metadata: A metadata object obtained after transmission - through the system under test. - - Raises: - AssertionError: if the transmitted_metadata object does not contain - original_metadata. - """ - raise NotImplementedError() - - def group_and_method(self): - """Returns the group and method used in this test case. - - Returns: - A pair of the group and method used in this test case. - """ - return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD - - def serialize_request(self, request): - """Serializes a request value used in this test case. - - Args: - request: A request value created by this test case. - - Returns: - A bytestring that is the serialization of the given request. - """ - return request - - def deserialize_request(self, serialized_request): - """Deserializes a request value used in this test case. - - Args: - serialized_request: A bytestring that is the serialization of some request - used in this test case. - - Returns: - The request value encoded by the given bytestring. - """ - return serialized_request - - def serialize_response(self, response): - """Serializes a response value used in this test case. - - Args: - response: A response value created by this test case. - - Returns: - A bytestring that is the serialization of the given response. - """ - return response - - def deserialize_response(self, serialized_response): - """Deserializes a response value used in this test case. - - Args: - serialized_response: A bytestring that is the serialization of some - response used in this test case. - - Returns: - The response value encoded by the given bytestring. - """ - return serialized_response - - def _assert_is_valid_metadata_payload_sequence( - self, ticket_sequence, payloads, initial_metadata, terminal_metadata): - initial_metadata_seen = False - seen_payloads = [] - terminal_metadata_seen = False - - for ticket in ticket_sequence: - if ticket.initial_metadata is not None: - self.assertFalse(initial_metadata_seen) - self.assertFalse(seen_payloads) - self.assertFalse(terminal_metadata_seen) - self.assertMetadataTransmitted(initial_metadata, ticket.initial_metadata) - initial_metadata_seen = True - - if ticket.payload is not None: - self.assertFalse(terminal_metadata_seen) - seen_payloads.append(ticket.payload) - - if ticket.terminal_metadata is not None: - self.assertFalse(terminal_metadata_seen) - self.assertMetadataTransmitted(terminal_metadata, ticket.terminal_metadata) - terminal_metadata_seen = True - self.assertSequenceEqual(payloads, seen_payloads) - - def _assert_is_valid_invocation_sequence( - self, ticket_sequence, group, method, payloads, initial_metadata, - terminal_metadata, termination): - self.assertLess(0, len(ticket_sequence)) - self.assertEqual(group, ticket_sequence[0].group) - self.assertEqual(method, ticket_sequence[0].method) - self._assert_is_valid_metadata_payload_sequence( - ticket_sequence, payloads, initial_metadata, terminal_metadata) - self.assertIs(termination, ticket_sequence[-1].termination) - - def _assert_is_valid_service_sequence( - self, ticket_sequence, payloads, initial_metadata, terminal_metadata, - code, message, termination): - self.assertLess(0, len(ticket_sequence)) - self._assert_is_valid_metadata_payload_sequence( - ticket_sequence, payloads, initial_metadata, terminal_metadata) - self.assertEqual(code, ticket_sequence[-1].code) - self.assertEqual(message, ticket_sequence[-1].message) - self.assertIs(termination, ticket_sequence[-1].termination) - - def setUp(self): - self._invocation_link, self._service_link = self.create_transmitting_links() - self._invocation_mate = test_utilities.RecordingLink() - self._service_mate = test_utilities.RecordingLink() - self._invocation_link.join_link(self._invocation_mate) - self._service_link.join_link(self._service_mate) - - def tearDown(self): - self.destroy_transmitting_links(self._invocation_link, self._service_link) - - def testSimplestRoundTrip(self): - """Tests transmission of one ticket in each direction.""" - invocation_operation_id = object() - invocation_payload = b'\x07' * 1023 - timeout = test_constants.LONG_TIMEOUT - invocation_initial_metadata = self.create_invocation_initial_metadata() - invocation_terminal_metadata = self.create_invocation_terminal_metadata() - invocation_code, invocation_message = self.create_invocation_completion() - service_payload = b'\x08' * 1025 - service_initial_metadata = self.create_service_initial_metadata() - service_terminal_metadata = self.create_service_terminal_metadata() - service_code, service_message = self.create_service_completion() - - original_invocation_ticket = links.Ticket( - invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, - links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, - invocation_payload, invocation_terminal_metadata, invocation_code, - invocation_message, links.Ticket.Termination.COMPLETION) - self._invocation_link.accept_ticket(original_invocation_ticket) - - # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the - # invocation-side ticket sequence shouldn't require granting allowance for - # another payload. - self._service_mate.block_until_tickets_satisfy( - at_least_n_payloads_received_predicate(1)) - service_operation_id = self._service_mate.tickets()[0].operation_id - self._service_link.accept_ticket( - links.Ticket( - service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, - None, 1, None, None, None, None, None, None)) - - self._service_mate.block_until_tickets_satisfy(terminated) - self._assert_is_valid_invocation_sequence( - self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, - (invocation_payload,), invocation_initial_metadata, - invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) - - original_service_ticket = links.Ticket( - service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, - timeout, 0, service_initial_metadata, service_payload, - service_terminal_metadata, service_code, service_message, - links.Ticket.Termination.COMPLETION) - self._service_link.accept_ticket(original_service_ticket) - self._invocation_mate.block_until_tickets_satisfy(terminated) - self._assert_is_valid_service_sequence( - self._invocation_mate.tickets(), (service_payload,), - service_initial_metadata, service_terminal_metadata, service_code, - service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/src/grpc/framework/interfaces/links/test_utilities.py b/src/python/src/grpc/framework/interfaces/links/test_utilities.py deleted file mode 100644 index 6c2e3346aa..0000000000 --- a/src/python/src/grpc/framework/interfaces/links/test_utilities.py +++ /dev/null @@ -1,66 +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. - -"""State and behavior appropriate for use in tests.""" - -import threading - -from grpc.framework.interfaces.links import links - - -class RecordingLink(links.Link): - """A Link that records every ticket passed to it.""" - - def __init__(self): - self._condition = threading.Condition() - self._tickets = [] - - def accept_ticket(self, ticket): - with self._condition: - self._tickets.append(ticket) - self._condition.notify_all() - - def join_link(self, link): - pass - - def block_until_tickets_satisfy(self, predicate): - """Blocks until the received tickets satisfy the given predicate. - - Args: - predicate: A callable that takes a sequence of tickets and returns a - boolean value. - """ - with self._condition: - while not predicate(self._tickets): - self._condition.wait() - - def tickets(self): - """Returns a copy of the list of all tickets received by this Link.""" - with self._condition: - return tuple(self._tickets) diff --git a/src/python/src/grpc/framework/interfaces/links/utilities.py b/src/python/src/grpc/framework/interfaces/links/utilities.py deleted file mode 100644 index 6e4fd76d93..0000000000 --- a/src/python/src/grpc/framework/interfaces/links/utilities.py +++ /dev/null @@ -1,44 +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. - -"""Utilities provided as part of the links interface.""" - -from grpc.framework.interfaces.links import links - - -class _NullLink(links.Link): - """A do-nothing links.Link.""" - - def accept_ticket(self, ticket): - pass - - def join_link(self, link): - pass - -NULL_LINK = _NullLink() diff --git a/src/python/src/setup.cfg b/src/python/src/setup.cfg deleted file mode 100644 index 8f69613632..0000000000 --- a/src/python/src/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[build_ext] -inplace=1 diff --git a/src/python/src/setup.py b/src/python/src/setup.py deleted file mode 100644 index 0310a83a7b..0000000000 --- a/src/python/src/setup.py +++ /dev/null @@ -1,130 +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. - -"""A setup module for the GRPC Python package.""" - -import os -import os.path -import sys - -from distutils import core as _core -import setuptools - -# Ensure we're in the proper directory whether or not we're being used by pip. -os.chdir(os.path.dirname(os.path.abspath(__file__))) - -# Break import-style to ensure we can actually find our commands module. -import commands - -# Use environment variables to determine whether or not the Cython extension -# should *use* Cython or use the generated C files. Note that this requires the -# C files to have been generated by building first *with* Cython support. -_BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) - -_C_EXTENSION_SOURCES = ( - '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 = ( - '.', -) - -_EXTENSION_LIBRARIES = ( - 'grpc', - 'gpr', -) -if not "darwin" in sys.platform: - _EXTENSION_LIBRARIES += ('rt',) - - -_C_EXTENSION_MODULE = _core.Extension( - 'grpc._adapter._c', sources=list(_C_EXTENSION_SOURCES), - include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES), - libraries=list(_EXTENSION_LIBRARIES), -) -_EXTENSION_MODULES = [_C_EXTENSION_MODULE] - -_PACKAGES = ( - 'grpc', - 'grpc._adapter', - 'grpc._junkdrawer', - 'grpc._links', - 'grpc.early_adopter', - 'grpc.framework', - 'grpc.framework.alpha', - 'grpc.framework.base', - 'grpc.framework.common', - 'grpc.framework.face', - 'grpc.framework.face.testing', - 'grpc.framework.foundation', - 'grpc.framework.interfaces', - 'grpc.framework.interfaces.links', -) - -_PACKAGE_DIRECTORIES = { - 'grpc': 'grpc', - 'grpc._adapter': 'grpc/_adapter', - 'grpc._junkdrawer': 'grpc/_junkdrawer', - 'grpc._links': 'grpc/_links', - 'grpc.early_adopter': 'grpc/early_adopter', - 'grpc.framework': 'grpc/framework', -} - -_INSTALL_REQUIRES = ( - 'enum34==1.0.4', - 'futures==2.2.0', - 'protobuf==3.0.0a3' -) - -_SETUP_REQUIRES = ( - 'sphinx>=1.3', -) + _INSTALL_REQUIRES - -_COMMAND_CLASS = { - 'doc': commands.SphinxDocumentation -} - -setuptools.setup( - name='grpcio', - version='0.10.0a0', - ext_modules=_EXTENSION_MODULES, - packages=list(_PACKAGES), - package_dir=_PACKAGE_DIRECTORIES, - install_requires=_INSTALL_REQUIRES, - setup_requires=_SETUP_REQUIRES, - cmdclass=_COMMAND_CLASS -) diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py index 3ab84a6ba1..d76792c56f 100755 --- a/tools/distrib/python/docgen.py +++ b/tools/distrib/python/docgen.py @@ -51,8 +51,8 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..')) CONFIG = args.config -SETUP_PATH = os.path.join(PROJECT_ROOT, 'src/python/src/setup.py') -DOC_PATH = os.path.join(PROJECT_ROOT, 'src/python/src/doc/build') +SETUP_PATH = os.path.join(PROJECT_ROOT, 'src/python/grpcio/setup.py') +DOC_PATH = os.path.join(PROJECT_ROOT, 'src/python/grpcio/doc/build') INCLUDE_PATH = os.path.join(PROJECT_ROOT, 'include') LIBRARY_PATH = os.path.join(PROJECT_ROOT, 'libs/{}'.format(CONFIG)) VIRTUALENV_DIR = os.path.join(SCRIPT_DIR, 'distrib_virtualenv') diff --git a/tools/distrib/python/submit.py b/tools/distrib/python/submit.py index a3615b3640..909ba56327 100755 --- a/tools/distrib/python/submit.py +++ b/tools/distrib/python/submit.py @@ -59,7 +59,7 @@ args = parser.parse_args() # Move to the root directory of Python GRPC. pkgdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../../src/python/src') + '../../../src/python/grpcio') # Remove previous distributions; they somehow confuse twine. try: shutil.rmtree(os.path.join(pkgdir, 'dist/')) diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh index ae0fb42241..265a542e3a 100755 --- a/tools/run_tests/build_python.sh +++ b/tools/run_tests/build_python.sh @@ -43,8 +43,8 @@ make_virtualenv() { virtualenv -p `which "python"$1` $virtualenv_name source $virtualenv_name/bin/activate pip install -r src/python/requirements.txt - CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/src - pip install src/python/interop + CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/grpcio + pip install src/python/grpcio_test else source $virtualenv_name/bin/activate # Uninstall and re-install the packages we care about. Don't use @@ -52,13 +52,13 @@ make_virtualenv() { # unnecessarily to dependencies. Don't use --no-deps to avoid missing # dependency upgrades. (yes | pip uninstall grpcio) || true - (yes | pip uninstall interop) || true - (CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/src) || ( + (yes | pip uninstall grpcio_test) || true + (CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/grpcio) || ( # Fall back to rebuilding the entire environment rm -rf $virtualenv_name make_virtualenv $1 ) - pip install src/python/interop + pip install src/python/grpcio_test fi } diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json index 3d75d8de36..8bb3939fdd 100755 --- a/tools/run_tests/python_tests.json +++ b/tools/run_tests/python_tests.json @@ -102,13 +102,13 @@ ] }, { - "module": "interop._insecure_interop_test", + "module": "grpc_interop._insecure_interop_test", "pythonVersions": [ "2.7" ] }, { - "module": "interop._secure_interop_test", + "module": "grpc_interop._secure_interop_test", "pythonVersions": [ "2.7" ] -- cgit v1.2.3 From 623b7aee4ac6471e3af13cd2256ad7605e54d983 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 27 Jul 2015 16:32:03 -0700 Subject: Reorganize Python tests --- .../_blocking_invocation_inline_service_test.py | 46 --- src/python/grpcio/grpc/_adapter/_c_test.py | 55 --- ...nt_invocation_synchronous_event_service_test.py | 46 --- src/python/grpcio/grpc/_adapter/_face_test_case.py | 106 ----- ...e_invocation_asynchronous_event_service_test.py | 46 --- .../grpcio/grpc/_adapter/_intermediary_low_test.py | 434 -------------------- src/python/grpcio/grpc/_adapter/_links_test.py | 277 ------------- .../grpcio/grpc/_adapter/_lonely_rear_link_test.py | 100 ----- src/python/grpcio/grpc/_adapter/_low_test.py | 199 --------- .../grpcio/grpc/_adapter/_proto_scenarios.py | 261 ------------ src/python/grpcio/grpc/_adapter/_test_links.py | 80 ---- src/python/grpcio/grpc/_cython/adapter_low_test.py | 187 --------- src/python/grpcio/grpc/_cython/cygrpc_test.py | 262 ------------ src/python/grpcio/grpc/_cython/test_utilities.py | 46 --- src/python/grpcio/grpc/_junkdrawer/__init__.py | 30 -- src/python/grpcio/grpc/_junkdrawer/math_pb2.py | 266 ------------ src/python/grpcio/grpc/_junkdrawer/stock_pb2.py | 152 ------- .../grpc/_links/_lonely_invocation_link_test.py | 88 ---- src/python/grpcio/grpc/_links/_proto_scenarios.py | 261 ------------ .../grpcio/grpc/_links/_transmission_test.py | 231 ----------- .../grpc/early_adopter/implementations_test.py | 180 --------- .../grpc/framework/base/implementations_test.py | 80 ---- .../grpc/framework/base/interfaces_test_case.py | 307 -------------- .../grpcio/grpc/framework/common/test_constants.py | 43 -- .../grpcio/grpc/framework/common/test_control.py | 87 ---- .../grpcio/grpc/framework/common/test_coverage.py | 116 ------ .../grpcio/grpc/framework/face/_test_case.py | 61 --- .../blocking_invocation_inline_service_test.py | 46 --- ...nt_invocation_synchronous_event_service_test.py | 46 --- ...e_invocation_asynchronous_event_service_test.py | 46 --- .../grpcio/grpc/framework/face/testing/__init__.py | 30 -- .../grpc/framework/face/testing/base_util.py | 102 ----- ...blocking_invocation_inline_service_test_case.py | 222 ---------- .../grpcio/grpc/framework/face/testing/callback.py | 94 ----- .../grpcio/grpc/framework/face/testing/control.py | 87 ---- .../grpcio/grpc/framework/face/testing/coverage.py | 123 ------ .../grpcio/grpc/framework/face/testing/digest.py | 450 --------------------- ...vocation_synchronous_event_service_test_case.py | 362 ----------------- ...ocation_asynchronous_event_service_test_case.py | 376 ----------------- .../grpc/framework/face/testing/interfaces.py | 117 ------ .../grpcio/grpc/framework/face/testing/serial.py | 70 ---- .../grpcio/grpc/framework/face/testing/service.py | 337 --------------- .../grpc/framework/face/testing/stock_service.py | 374 ----------------- .../grpc/framework/face/testing/test_case.py | 80 ---- .../grpc/framework/foundation/_later_test.py | 151 ------- .../framework/foundation/_logging_pool_test.py | 64 --- .../grpc/framework/foundation/stream_testing.py | 73 ---- .../grpc/framework/interfaces/links/test_cases.py | 333 --------------- .../framework/interfaces/links/test_utilities.py | 66 --- src/python/grpcio_test/grpc_test/__init__.py | 30 ++ .../grpcio_test/grpc_test/_adapter/.gitignore | 5 + .../grpcio_test/grpc_test/_adapter/__init__.py | 30 ++ .../_blocking_invocation_inline_service_test.py | 46 +++ .../grpcio_test/grpc_test/_adapter/_c_test.py | 55 +++ ...nt_invocation_synchronous_event_service_test.py | 46 +++ .../grpc_test/_adapter/_face_test_case.py | 106 +++++ ...e_invocation_asynchronous_event_service_test.py | 46 +++ .../grpc_test/_adapter/_intermediary_low_test.py | 434 ++++++++++++++++++++ .../grpcio_test/grpc_test/_adapter/_links_test.py | 277 +++++++++++++ .../grpc_test/_adapter/_lonely_rear_link_test.py | 100 +++++ .../grpcio_test/grpc_test/_adapter/_low_test.py | 199 +++++++++ .../grpc_test/_adapter/_proto_scenarios.py | 261 ++++++++++++ .../grpcio_test/grpc_test/_adapter/_test_links.py | 80 ++++ .../grpcio_test/grpc_test/_cython/.gitignore | 7 + .../grpcio_test/grpc_test/_cython/__init__.py | 28 ++ .../grpc_test/_cython/adapter_low_test.py | 187 +++++++++ .../grpcio_test/grpc_test/_cython/cygrpc_test.py | 262 ++++++++++++ .../grpc_test/_cython/test_utilities.py | 46 +++ .../grpcio_test/grpc_test/_junkdrawer/__init__.py | 30 ++ .../grpcio_test/grpc_test/_junkdrawer/math_pb2.py | 266 ++++++++++++ .../grpcio_test/grpc_test/_junkdrawer/stock_pb2.py | 152 +++++++ .../grpcio_test/grpc_test/_links/__init__.py | 30 ++ .../_links/_lonely_invocation_link_test.py | 88 ++++ .../grpc_test/_links/_proto_scenarios.py | 261 ++++++++++++ .../grpc_test/_links/_transmission_test.py | 231 +++++++++++ .../grpc_test/early_adopter/__init__.py | 30 ++ .../early_adopter/implementations_test.py | 180 +++++++++ .../grpcio_test/grpc_test/framework/__init__.py | 30 ++ .../grpc_test/framework/base/__init__.py | 30 ++ .../framework/base/implementations_test.py | 80 ++++ .../framework/base/interfaces_test_case.py | 307 ++++++++++++++ .../grpc_test/framework/common/__init__.py | 30 ++ .../grpc_test/framework/common/test_constants.py | 43 ++ .../grpc_test/framework/common/test_control.py | 87 ++++ .../grpc_test/framework/common/test_coverage.py | 116 ++++++ .../grpc_test/framework/face/__init__.py | 30 ++ .../grpc_test/framework/face/_test_case.py | 61 +++ .../blocking_invocation_inline_service_test.py | 46 +++ ...nt_invocation_synchronous_event_service_test.py | 46 +++ ...e_invocation_asynchronous_event_service_test.py | 46 +++ .../grpc_test/framework/face/testing/__init__.py | 30 ++ .../grpc_test/framework/face/testing/base_util.py | 102 +++++ ...blocking_invocation_inline_service_test_case.py | 222 ++++++++++ .../grpc_test/framework/face/testing/callback.py | 94 +++++ .../grpc_test/framework/face/testing/control.py | 87 ++++ .../grpc_test/framework/face/testing/coverage.py | 123 ++++++ .../grpc_test/framework/face/testing/digest.py | 450 +++++++++++++++++++++ ...vocation_synchronous_event_service_test_case.py | 362 +++++++++++++++++ ...ocation_asynchronous_event_service_test_case.py | 376 +++++++++++++++++ .../grpc_test/framework/face/testing/interfaces.py | 117 ++++++ .../grpc_test/framework/face/testing/serial.py | 70 ++++ .../grpc_test/framework/face/testing/service.py | 337 +++++++++++++++ .../framework/face/testing/stock_service.py | 374 +++++++++++++++++ .../grpc_test/framework/face/testing/test_case.py | 80 ++++ .../grpc_test/framework/foundation/__init__.py | 30 ++ .../grpc_test/framework/foundation/_later_test.py | 151 +++++++ .../framework/foundation/_logging_pool_test.py | 64 +++ .../framework/foundation/stream_testing.py | 73 ++++ .../grpc_test/framework/interfaces/__init__.py | 30 ++ .../framework/interfaces/links/__init__.py | 30 ++ .../framework/interfaces/links/test_cases.py | 333 +++++++++++++++ .../framework/interfaces/links/test_utilities.py | 66 +++ src/python/grpcio_test/setup.py | 2 +- tools/run_tests/python_tests.json | 34 +- 114 files changed, 8084 insertions(+), 7714 deletions(-) delete mode 100644 src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_c_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_face_test_case.py delete mode 100644 src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_intermediary_low_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_links_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_low_test.py delete mode 100644 src/python/grpcio/grpc/_adapter/_proto_scenarios.py delete mode 100644 src/python/grpcio/grpc/_adapter/_test_links.py delete mode 100644 src/python/grpcio/grpc/_cython/adapter_low_test.py delete mode 100644 src/python/grpcio/grpc/_cython/cygrpc_test.py delete mode 100644 src/python/grpcio/grpc/_cython/test_utilities.py delete mode 100644 src/python/grpcio/grpc/_junkdrawer/__init__.py delete mode 100644 src/python/grpcio/grpc/_junkdrawer/math_pb2.py delete mode 100644 src/python/grpcio/grpc/_junkdrawer/stock_pb2.py delete mode 100644 src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py delete mode 100644 src/python/grpcio/grpc/_links/_proto_scenarios.py delete mode 100644 src/python/grpcio/grpc/_links/_transmission_test.py delete mode 100644 src/python/grpcio/grpc/early_adopter/implementations_test.py delete mode 100644 src/python/grpcio/grpc/framework/base/implementations_test.py delete mode 100644 src/python/grpcio/grpc/framework/base/interfaces_test_case.py delete mode 100644 src/python/grpcio/grpc/framework/common/test_constants.py delete mode 100644 src/python/grpcio/grpc/framework/common/test_control.py delete mode 100644 src/python/grpcio/grpc/framework/common/test_coverage.py delete mode 100644 src/python/grpcio/grpc/framework/face/_test_case.py delete mode 100644 src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py delete mode 100644 src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py delete mode 100644 src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/__init__.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/base_util.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/callback.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/control.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/coverage.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/digest.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/interfaces.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/serial.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/service.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/stock_service.py delete mode 100644 src/python/grpcio/grpc/framework/face/testing/test_case.py delete mode 100644 src/python/grpcio/grpc/framework/foundation/_later_test.py delete mode 100644 src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py delete mode 100644 src/python/grpcio/grpc/framework/foundation/stream_testing.py delete mode 100644 src/python/grpcio/grpc/framework/interfaces/links/test_cases.py delete mode 100644 src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py create mode 100644 src/python/grpcio_test/grpc_test/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/.gitignore create mode 100644 src/python/grpcio_test/grpc_test/_adapter/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_c_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_links_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py create mode 100644 src/python/grpcio_test/grpc_test/_adapter/_test_links.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/.gitignore create mode 100644 src/python/grpcio_test/grpc_test/_cython/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py create mode 100644 src/python/grpcio_test/grpc_test/_cython/test_utilities.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py create mode 100644 src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py create mode 100644 src/python/grpcio_test/grpc_test/_links/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py create mode 100644 src/python/grpcio_test/grpc_test/_links/_transmission_test.py create mode 100644 src/python/grpcio_test/grpc_test/early_adopter/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/implementations_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_constants.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_control.py create mode 100644 src/python/grpcio_test/grpc_test/framework/common/test_coverage.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/callback.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/control.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/digest.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/serial.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py create mode 100644 src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py create mode 100644 src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py create mode 100644 src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py (limited to 'src/python') diff --git a/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py b/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py deleted file mode 100644 index 7a8ff0ad89..0000000000 --- a/src/python/grpcio/grpc/_adapter/_blocking_invocation_inline_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case - - -class BlockingInvocationInlineServiceTest( - _face_test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_c_test.py b/src/python/grpcio/grpc/_adapter/_c_test.py deleted file mode 100644 index fe020e2a9c..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c_test.py +++ /dev/null @@ -1,55 +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. - -import time -import unittest - -from grpc._adapter import _c -from grpc._adapter import _types - - -class CTypeSmokeTest(unittest.TestCase): - - def testCompletionQueueUpDown(self): - completion_queue = _c.CompletionQueue() - del completion_queue - - def testServerUpDown(self): - completion_queue = _c.CompletionQueue() - serv = _c.Server(completion_queue, []) - del serv - del completion_queue - - def testChannelUpDown(self): - channel = _c.Channel('[::]:0', []) - del channel - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py deleted file mode 100644 index b8ceb75d68..0000000000 --- a/src/python/grpcio/grpc/_adapter/_event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_face_test_case.py b/src/python/grpcio/grpc/_adapter/_face_test_case.py deleted file mode 100644 index 5fa974ed06..0000000000 --- a/src/python/grpcio/grpc/_adapter/_face_test_case.py +++ /dev/null @@ -1,106 +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. - -"""Common construction and destruction for GRPC-backed Face-layer tests.""" - -import unittest - -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import util -from grpc.framework.base import implementations as base_implementations -from grpc.framework.face import implementations as face_implementations -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import serial -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_TIMEOUT = 90 -_MAXIMUM_POOL_SIZE = 4 - - -class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): - """Provides abstract Face-layer tests a GRPC-backed implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = face_implementations.servicer( - pool, method_implementations, multi_method_implementation) - - serialization = serial.serialization(methods) - - fore_link = fore.ForeLink( - pool, serialization.request_deserializers, - serialization.response_serializers, None, ()) - fore_link.start() - port = fore_link.port() - rear_link = rear.RearLink( - 'localhost', port, pool, - serialization.request_serializers, - serialization.response_deserializers, False, None, None, None) - rear_link.start() - front = base_implementations.front_link(pool, pool, pool) - back = base_implementations.back_link( - servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) - fore_link.join_rear_link(back) - back.join_fore_link(fore_link) - rear_link.join_fore_link(front) - front.join_rear_link(rear_link) - - stub = face_implementations.generic_stub(front, pool) - return stub, (rear_link, fore_link, front, back) - - def tear_down_implementation(self, memo): - rear_link, fore_link, front, back = memo - # TODO(nathaniel): Waiting for the front and back to idle possibly should - # not be necessary - investigate as part of graceful shutdown work. - util.wait_for_idle(front) - util.wait_for_idle(back) - rear_link.stop() - fore_link.stop() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Service-side failure not transmitted by GRPC.') - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index 3773e65575..0000000000 --- a/src/python/grpcio/grpc/_adapter/_future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc._adapter import _face_test_case -from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _face_test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py b/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py deleted file mode 100644 index 27a5b82e9c..0000000000 --- a/src/python/grpcio/grpc/_adapter/_intermediary_low_test.py +++ /dev/null @@ -1,434 +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. - -"""Tests for the old '_low'.""" - -import Queue -import threading -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 - - -def _drive_completion_queue(completion_queue, event_queue): - while True: - event = completion_queue.get(_FUTURE) - if event.kind is _low.Event.Kind.STOP: - break - event_queue.put(event) - - -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.server_events = Queue.Queue() - self.server_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.server_completion_queue, self.server_events)) - self.server_completion_queue_thread.start() - - self.client_completion_queue = _low.CompletionQueue() - self.channel = _low.Channel('%s:%d' % (self.host, port), None) - self.client_events = Queue.Queue() - self.client_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.client_completion_queue, self.client_events)) - self.client_completion_queue_thread.start() - - def tearDown(self): - self.server.stop() - self.server_completion_queue.stop() - self.client_completion_queue.stop() - self.server_completion_queue_thread.join() - self.client_completion_queue_thread.join() - del self.server - - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - 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_events.get() - server_terminal_event_two = self.server_events.get() - 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_events.get() - client_terminal_event_two = self.client_events.get() - 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]) - self.assertSetEqual(set(key for key, _ in finish_accepted.metadata), - set((server_trailing_metadata_key, - 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.server_events = Queue.Queue() - self.server_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.server_completion_queue, self.server_events)) - self.server_completion_queue_thread.start() - - self.client_completion_queue = _low.CompletionQueue() - self.channel = _low.Channel('%s:%d' % (self.host, port), None) - self.client_events = Queue.Queue() - self.client_completion_queue_thread = threading.Thread( - target=_drive_completion_queue, - args=(self.client_completion_queue, self.client_events)) - self.client_completion_queue_thread.start() - - def tearDown(self): - self.server.stop() - self.server_completion_queue.stop() - self.client_completion_queue.stop() - self.server_completion_queue_thread.join() - self.client_completion_queue_thread.join() - del self.server - - 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_events.get() - server_call = service_accepted.service_acceptance.call - - server_call.accept(self.server_completion_queue, finish_tag) - server_call.premetadata() - - metadata_accepted = self.client_events.get() - self.assertIsNotNone(metadata_accepted) - - for datum in test_data: - client_call.write(datum, write_tag) - write_accepted = self.client_events.get() - - server_call.read(read_tag) - read_accepted = self.server_events.get() - server_data.append(read_accepted.bytes) - - server_call.write(read_accepted.bytes, write_tag) - write_accepted = self.server_events.get() - self.assertIsNotNone(write_accepted) - - client_call.read(read_tag) - read_accepted = self.client_events.get() - 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_events.get() - server_terminal_event_two = self.server_events.get() - 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_events.get() - 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) - diff --git a/src/python/grpcio/grpc/_adapter/_links_test.py b/src/python/grpcio/grpc/_adapter/_links_test.py deleted file mode 100644 index 4729b84f84..0000000000 --- a/src/python/grpcio/grpc/_adapter/_links_test.py +++ /dev/null @@ -1,277 +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. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import threading -import unittest - -from grpc._adapter import _proto_scenarios -from grpc._adapter import _test_links -from grpc._adapter import fore -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool - -_IDENTITY = lambda x: x -_TIMEOUT = 32 - - -# TODO(nathaniel): End-to-end metadata testing. -def _transform_metadata(unused_metadata): - return ( - ('one unused key', 'one unused value'), - ('another unused key', 'another unused value'), -) - - -class RoundTripTest(unittest.TestCase): - - def setUp(self): - self.fore_link_pool = logging_pool.pool(8) - self.rear_link_pool = logging_pool.pool(8) - - def tearDown(self): - self.rear_link_pool.shutdown(wait=True) - self.fore_link_pool.shutdown(wait=True) - - def testZeroMessageRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_fore_link = _test_links.ForeLink(None, None) - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE): - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, 0, - interfaces.BackToFrontTicket.Kind.COMPLETION, None) - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: None}, - {test_method: None}, False, None, None, None, - metadata_transformer=_transform_metadata) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is - interfaces.BackToFrontTicket.Kind.CONTINUATION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_fore_link.condition: - self.assertIs( - test_fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testEntireRoundTrip(self): - test_operation_id = object() - test_method = 'test method' - test_front_to_back_datum = b'\x07' - test_back_to_front_datum = b'\x08' - test_fore_link = _test_links.ForeLink(None, None) - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - if front_to_back_ticket.payload is None: - payload = None - else: - payload = test_back_to_front_datum - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if payload is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - payload) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, - test_method, interfaces.ServicedSubscription.Kind.FULL, None, - test_front_to_back_datum, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - front_to_back_payloads = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - back_to_front_payloads = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) - self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) - - def _perform_scenario_test(self, scenario): - test_operation_id = object() - test_method = scenario.method() - test_fore_link = _test_links.ForeLink(None, None) - rear_lock = threading.Lock() - rear_sequence_number = [0] - def rear_action(front_to_back_ticket, fore_link): - with rear_lock: - if front_to_back_ticket.payload is not None: - response = scenario.response_for_request(front_to_back_ticket.payload) - else: - response = None - terminal = front_to_back_ticket.kind in ( - interfaces.FrontToBackTicket.Kind.COMPLETION, - interfaces.FrontToBackTicket.Kind.ENTIRE) - if response is not None or terminal: - if terminal: - kind = interfaces.BackToFrontTicket.Kind.COMPLETION - else: - kind = interfaces.BackToFrontTicket.Kind.CONTINUATION - back_to_front_ticket = interfaces.BackToFrontTicket( - front_to_back_ticket.operation_id, rear_sequence_number[0], kind, - response) - rear_sequence_number[0] += 1 - fore_link.accept_back_to_front_ticket(back_to_front_ticket) - test_rear_link = _test_links.RearLink(rear_action, None) - - fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: scenario.deserialize_request}, - {test_method: scenario.serialize_response}, None, ()) - fore_link.join_rear_link(test_rear_link) - test_rear_link.join_fore_link(fore_link) - fore_link.start() - port = fore_link.port() - - rear_link = rear.RearLink( - 'localhost', port, self.rear_link_pool, - {test_method: scenario.serialize_request}, - {test_method: scenario.deserialize_response}, False, None, None, None) - rear_link.join_fore_link(test_fore_link) - test_fore_link.join_rear_link(rear_link) - rear_link.start() - - commencement_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, - interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, - _TIMEOUT) - fore_sequence_number = 1 - rear_link.accept_front_to_back_ticket(commencement_ticket) - for request in scenario.requests(): - continuation_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, - request, None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(continuation_ticket) - completion_ticket = interfaces.FrontToBackTicket( - test_operation_id, fore_sequence_number, - interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, - None) - fore_sequence_number += 1 - rear_link.accept_front_to_back_ticket(completion_ticket) - - with test_fore_link.condition: - while (not test_fore_link.tickets or - test_fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.COMPLETION): - test_fore_link.condition.wait() - - rear_link.stop() - fore_link.stop() - - with test_rear_link.condition: - requests = tuple( - ticket.payload for ticket in test_rear_link.tickets - if ticket.payload is not None) - with test_fore_link.condition: - responses = tuple( - ticket.payload for ticket in test_fore_link.tickets - if ticket.payload is not None) - self.assertTrue(scenario.verify_requests(requests)) - self.assertTrue(scenario.verify_responses(responses)) - - def testEmptyScenario(self): - self._perform_scenario_test(_proto_scenarios.EmptyScenario()) - - def testBidirectionallyUnaryScenario(self): - self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) - - def testBidirectionallyStreamingScenario(self): - self._perform_scenario_test( - _proto_scenarios.BidirectionallyStreamingScenario()) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py b/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py deleted file mode 100644 index 7f5021f40e..0000000000 --- a/src/python/grpcio/grpc/_adapter/_lonely_rear_link_test.py +++ /dev/null @@ -1,100 +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. - -"""A test of invocation-side code unconnected to an RPC server.""" - -import unittest - -from grpc._adapter import _test_links -from grpc._adapter import rear -from grpc.framework.base import interfaces -from grpc.framework.foundation import logging_pool - -_IDENTITY = lambda x: x -_TIMEOUT = 2 - - -class LonelyRearLinkTest(unittest.TestCase): - - def setUp(self): - self.pool = logging_pool.pool(8) - - def tearDown(self): - self.pool.shutdown(wait=True) - - def testUpAndDown(self): - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) - - rear_link.start() - rear_link.stop() - - def _perform_lonely_client_test_with_ticket_kind( - self, front_to_back_ticket_kind): - test_operation_id = object() - test_method = 'test method' - fore_link = _test_links.ForeLink(None, None) - - rear_link = rear.RearLink( - 'nonexistent', 54321, self.pool, {test_method: None}, - {test_method: None}, False, None, None, None) - rear_link.join_fore_link(fore_link) - rear_link.start() - - front_to_back_ticket = interfaces.FrontToBackTicket( - test_operation_id, 0, front_to_back_ticket_kind, test_method, - interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) - rear_link.accept_front_to_back_ticket(front_to_back_ticket) - - with fore_link.condition: - while True: - if (fore_link.tickets and - fore_link.tickets[-1].kind is not - interfaces.BackToFrontTicket.Kind.CONTINUATION): - break - fore_link.condition.wait() - - rear_link.stop() - - with fore_link.condition: - self.assertIsNot( - fore_link.tickets[-1].kind, - interfaces.BackToFrontTicket.Kind.COMPLETION) - - def testLonelyClientCommencementTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.COMMENCEMENT) - - def testLonelyClientEntireTicket(self): - self._perform_lonely_client_test_with_ticket_kind( - interfaces.FrontToBackTicket.Kind.ENTIRE) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_low_test.py b/src/python/grpcio/grpc/_adapter/_low_test.py deleted file mode 100644 index 9a8edfad0c..0000000000 --- a/src/python/grpcio/grpc/_adapter/_low_test.py +++ /dev/null @@ -1,199 +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. - -import threading -import time -import unittest - -from grpc._adapter import _types -from grpc._adapter import _low - - -def WaitForEvents(completion_queues, deadline): - """ - Args: - completion_queues: list of completion queues to wait for events on - deadline: absolute deadline to wait until - - Returns: - a sequence of events of length len(completion_queues). - """ - - results = [None] * len(completion_queues) - lock = threading.Lock() - threads = [] - def set_ith_result(i, completion_queue): - result = completion_queue.next(deadline) - with lock: - print i, completion_queue, result, time.time() - deadline - results[i] = result - for i, completion_queue in enumerate(completion_queues): - thread = threading.Thread(target=set_ith_result, - args=[i, completion_queue]) - thread.start() - threads.append(thread) - for thread in threads: - thread.join() - return results - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue, []) - self.port = self.server.add_http2_port('[::]:0') - self.client_completion_queue = _low.CompletionQueue() - self.client_channel = _low.Channel('localhost:%d'%self.port, []) - - self.server.start() - - def tearDown(self): - self.server.shutdown() - del self.client_channel - - 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 - del self.server - - 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) - - client_no_event, request_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 2) - self.assertEquals(client_no_event, None) - 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)) - got_initial_metadata = dict(request_event.results[0].initial_metadata) - self.assertEquals( - dict(client_initial_metadata), - dict((x, got_initial_metadata[x]) for x in zip(*client_initial_metadata)[0])) - 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, server_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 1) - - 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__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_adapter/_proto_scenarios.py b/src/python/grpcio/grpc/_adapter/_proto_scenarios.py deleted file mode 100644 index 60a622ba8b..0000000000 --- a/src/python/grpcio/grpc/_adapter/_proto_scenarios.py +++ /dev/null @@ -1,261 +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. - -"""Test scenarios using protocol buffers.""" - -import abc -import threading - -from grpc._junkdrawer import math_pb2 - - -class ProtoScenario(object): - """An RPC test scenario using protocol buffers.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def method(self): - """Access the test method name. - - Returns: - The test method name. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize a request protocol buffer. - - Args: - request: A request protocol buffer. - - Returns: - The bytestring serialization of the given request protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, request_bytestring): - """Deserialize a request protocol buffer. - - Args: - request_bytestring: The bytestring serialization of a request protocol - buffer. - - Returns: - The request protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize a response protocol buffer. - - Args: - response: A response protocol buffer. - - Returns: - The bytestring serialization of the given response protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, response_bytestring): - """Deserialize a response protocol buffer. - - Args: - response_bytestring: The bytestring serialization of a response protocol - buffer. - - Returns: - The response protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def requests(self): - """Access the sequence of requests for this scenario. - - Returns: - A sequence of request protocol buffers. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_for_request(self, request): - """Access the response for a particular request. - - Args: - request: A request protocol buffer. - - Returns: - The response protocol buffer appropriate for the given request. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_requests(self, experimental_requests): - """Verify the requests transmitted through the system under test. - - Args: - experimental_requests: The request protocol buffers transmitted through - the system under test. - - Returns: - True if the requests satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_responses(self, experimental_responses): - """Verify the responses transmitted through the system under test. - - Args: - experimental_responses: The response protocol buffers transmitted through - the system under test. - - Returns: - True if the responses satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - -class EmptyScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - def method(self): - return 'DivMany' - - def serialize_request(self, request): - raise ValueError('This should not be necessary to call!') - - def deserialize_request(self, request_bytestring): - raise ValueError('This should not be necessary to call!') - - def serialize_response(self, response): - raise ValueError('This should not be necessary to call!') - - def deserialize_response(self, response_bytestring): - raise ValueError('This should not be necessary to call!') - - def requests(self): - return () - - def response_for_request(self, request): - raise ValueError('This should not be necessary to call!') - - def verify_requests(self, experimental_requests): - return not experimental_requests - - def verify_responses(self, experimental_responses): - return not experimental_responses - - -class BidirectionallyUnaryScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _DIVIDEND = 59 - _DIVISOR = 7 - _QUOTIENT = 8 - _REMAINDER = 3 - - _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) - _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) - - def method(self): - return 'Div' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return [self._REQUEST] - - def response_for_request(self, request): - return self._RESPONSE - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == (self._REQUEST,) - - def verify_responses(self, experimental_responses): - return tuple(experimental_responses) == (self._RESPONSE,) - - -class BidirectionallyStreamingScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _STREAM_LENGTH = 200 - _REQUESTS = tuple( - math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) - for index in range(_STREAM_LENGTH)) - - def __init__(self): - self._lock = threading.Lock() - self._responses = [] - - def method(self): - return 'DivMany' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return self._REQUESTS - - def response_for_request(self, request): - quotient, remainder = divmod(request.dividend, request.divisor) - response = math_pb2.DivReply(quotient=quotient, remainder=remainder) - with self._lock: - self._responses.append(response) - return response - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == self._REQUESTS - - def verify_responses(self, experimental_responses): - with self._lock: - return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio/grpc/_adapter/_test_links.py b/src/python/grpcio/grpc/_adapter/_test_links.py deleted file mode 100644 index 86c7e61b17..0000000000 --- a/src/python/grpcio/grpc/_adapter/_test_links.py +++ /dev/null @@ -1,80 +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. - -"""Links suitable for use in tests.""" - -import threading - -from grpc.framework.base import interfaces - - -class ForeLink(interfaces.ForeLink): - """A ForeLink suitable for use in tests of RearLinks.""" - - def __init__(self, action, rear_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.rear_link = rear_link - - def accept_back_to_front_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, rear_link = self.action, self.rear_link - - if action is not None: - action(ticket, rear_link) - - def join_rear_link(self, rear_link): - with self.condition: - self.rear_link = rear_link - - -class RearLink(interfaces.RearLink): - """A RearLink suitable for use in tests of ForeLinks.""" - - def __init__(self, action, fore_link): - self.condition = threading.Condition() - self.tickets = [] - self.action = action - self.fore_link = fore_link - - def accept_front_to_back_ticket(self, ticket): - with self.condition: - self.tickets.append(ticket) - self.condition.notify_all() - action, fore_link = self.action, self.fore_link - - if action is not None: - action(ticket, fore_link) - - def join_fore_link(self, fore_link): - with self.condition: - self.fore_link = fore_link diff --git a/src/python/grpcio/grpc/_cython/adapter_low_test.py b/src/python/grpcio/grpc/_cython/adapter_low_test.py deleted file mode 100644 index 9bab930e56..0000000000 --- a/src/python/grpcio/grpc/_cython/adapter_low_test.py +++ /dev/null @@ -1,187 +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. - -# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in -# grpc._cython.low should transparently support the semantics expected of -# grpc._adapter._low. - -import time -import unittest - -from grpc._adapter import _types -from grpc._cython import adapter_low as _low - - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue, []) - self.port = self.server.add_http2_port('[::]:0') - self.client_completion_queue = _low.CompletionQueue() - self.client_channel = _low.Channel('localhost:%d'%self.port, []) - - self.server.start() - - def tearDown(self): - self.server.shutdown() - del self.client_channel - - 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 - del self.server - - @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') - 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.assertEqual(_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.assertEqual(_types.CallError.OK, client_start_batch_result) - - request_event = self.server_completion_queue.next(DEADLINE) - self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) - self.assertIsInstance(request_event.call, _low.Call) - self.assertIs(server_request_tag, request_event.tag) - self.assertEqual(1, len(request_event.results)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.results[0].initial_metadata)) - self.assertEqual(METHOD, request_event.call_details.method) - self.assertEqual(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.assertEqual(_types.CallError.OK, server_start_batch_result) - - client_event = self.client_completion_queue.next(DEADLINE) - server_event = self.server_completion_queue.next(DEADLINE) - - self.assertEqual(6, len(client_event.results)) - found_client_op_types = set() - for client_result in client_event.results: - # we expect each op type to be unique - self.assertNotIn(client_result.type, found_client_op_types) - found_client_op_types.add(client_result.type) - if client_result.type == _types.OpType.RECV_INITIAL_METADATA: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.initial_metadata)) - elif client_result.type == _types.OpType.RECV_MESSAGE: - self.assertEqual(RESPONSE, client_result.message) - elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.trailing_metadata)) - self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) - self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) - self.assertEqual(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.assertEqual(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.assertEqual(REQUEST, server_result.message) - elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: - self.assertFalse(server_result.cancelled) - self.assertEqual(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__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_cython/cygrpc_test.py b/src/python/grpcio/grpc/_cython/cygrpc_test.py deleted file mode 100644 index 22d210b16b..0000000000 --- a/src/python/grpcio/grpc/_cython/cygrpc_test.py +++ /dev/null @@ -1,262 +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. - -import time -import unittest - -from grpc._cython import cygrpc -from grpc._cython import test_utilities - - -class TypeSmokeTest(unittest.TestCase): - - def testStringsInUtilitiesUpDown(self): - self.assertEqual(0, cygrpc.StatusCode.ok) - metadatum = cygrpc.Metadatum('a', 'b') - self.assertEqual('a'.encode(), metadatum.key) - self.assertEqual('b'.encode(), metadatum.value) - metadata = cygrpc.Metadata([metadatum]) - self.assertEqual(1, len(metadata)) - self.assertEqual(metadatum.key, metadata[0].key) - - def testMetadataIteration(self): - metadata = cygrpc.Metadata([ - cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) - iterator = iter(metadata) - metadatum = next(iterator) - self.assertIsInstance(metadatum, cygrpc.Metadatum) - self.assertEqual(metadatum.key, 'a'.encode()) - self.assertEqual(metadatum.value, 'b'.encode()) - metadatum = next(iterator) - self.assertIsInstance(metadatum, cygrpc.Metadatum) - self.assertEqual(metadatum.key, 'c'.encode()) - self.assertEqual(metadatum.value, 'd'.encode()) - with self.assertRaises(StopIteration): - next(iterator) - - def testOperationsIteration(self): - operations = cygrpc.Operations([ - cygrpc.operation_send_message('asdf')]) - iterator = iter(operations) - operation = next(iterator) - self.assertIsInstance(operation, cygrpc.Operation) - # `Operation`s are write-only structures; can't directly debug anything out - # of them. Just check that we stop iterating. - with self.assertRaises(StopIteration): - next(iterator) - - def testTimespec(self): - now = time.time() - timespec = cygrpc.Timespec(now) - self.assertAlmostEqual(now, float(timespec), places=8) - - def testCompletionQueueUpDown(self): - completion_queue = cygrpc.CompletionQueue() - del completion_queue - - def testServerUpDown(self): - server = cygrpc.Server(cygrpc.ChannelArgs([])) - del server - - def testChannelUpDown(self): - channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) - del channel - - @unittest.skip('TODO(atash): undo skip after #2229 is merged') - def testServerStartNoExplicitShutdown(self): - server = cygrpc.Server() - completion_queue = cygrpc.CompletionQueue() - server.register_completion_queue(completion_queue) - port = server.add_http2_port('[::]:0') - self.assertIsInstance(port, int) - server.start() - del server - - @unittest.skip('TODO(atash): undo skip after #2229 is merged') - def testServerStartShutdown(self): - completion_queue = cygrpc.CompletionQueue() - server = cygrpc.Server() - server.add_http2_port('[::]:0') - server.register_completion_queue(completion_queue) - server.start() - shutdown_tag = object() - server.shutdown(completion_queue, shutdown_tag) - event = completion_queue.poll() - self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) - self.assertIs(shutdown_tag, event.tag) - del server - del completion_queue - - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = cygrpc.CompletionQueue() - self.server = cygrpc.Server() - self.server.register_completion_queue(self.server_completion_queue) - self.port = self.server.add_http2_port('[::]:0') - self.server.start() - self.client_completion_queue = cygrpc.CompletionQueue() - self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) - - def tearDown(self): - del self.server - 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 = b'key' - CLIENT_METADATA_ASCII_VALUE = b'val' - CLIENT_METADATA_BIN_KEY = b'key-bin' - CLIENT_METADATA_BIN_VALUE = b'\0'*1000 - SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' - SERVER_INITIAL_METADATA_VALUE = b'whodawha?' - SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' - SERVER_TRAILING_METADATA_VALUE = b'zomg it is' - SERVER_STATUS_CODE = cygrpc.StatusCode.ok - SERVER_STATUS_DETAILS = b'our work is never over' - REQUEST = b'in death a member of project mayhem has a name' - RESPONSE = b'his name is robert paulson' - METHOD = b'twinkies' - HOST = b'hostess' - - cygrpc_deadline = cygrpc.Timespec(DEADLINE) - - server_request_tag = object() - request_call_result = self.server.request_call( - self.server_completion_queue, self.server_completion_queue, - server_request_tag) - - self.assertEqual(cygrpc.CallError.ok, request_call_result) - - client_call_tag = object() - client_call = self.client_channel.create_call(self.client_completion_queue, - METHOD, HOST, cygrpc_deadline) - client_initial_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, - CLIENT_METADATA_ASCII_VALUE), - cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) - client_start_batch_result = client_call.start_batch(cygrpc.Operations([ - cygrpc.operation_send_initial_metadata(client_initial_metadata), - cygrpc.operation_send_message(REQUEST), - cygrpc.operation_send_close_from_client(), - cygrpc.operation_receive_initial_metadata(), - cygrpc.operation_receive_message(), - cygrpc.operation_receive_status_on_client() - ]), client_call_tag) - self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) - client_event_future = test_utilities.CompletionQueuePollFuture( - self.client_completion_queue, cygrpc_deadline) - - request_event = self.server_completion_queue.poll(cygrpc_deadline) - self.assertEqual(cygrpc.CompletionType.operation_complete, - request_event.type) - self.assertIsInstance(request_event.operation_call, cygrpc.Call) - self.assertIs(server_request_tag, request_event.tag) - self.assertEqual(0, len(request_event.batch_operations)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.request_metadata)) - self.assertEqual(METHOD, request_event.request_call_details.method) - self.assertEqual(HOST, request_event.request_call_details.host) - self.assertLess( - abs(DEADLINE - float(request_event.request_call_details.deadline)), - DEADLINE_TOLERANCE) - - server_call_tag = object() - server_call = request_event.operation_call - server_initial_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, - SERVER_INITIAL_METADATA_VALUE)]) - server_trailing_metadata = cygrpc.Metadata([ - cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, - SERVER_TRAILING_METADATA_VALUE)]) - server_start_batch_result = server_call.start_batch([ - cygrpc.operation_send_initial_metadata(server_initial_metadata), - cygrpc.operation_receive_message(), - cygrpc.operation_send_message(RESPONSE), - cygrpc.operation_receive_close_on_server(), - cygrpc.operation_send_status_from_server( - server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) - ], server_call_tag) - self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) - - client_event = client_event_future.result() - server_event = self.server_completion_queue.poll(cygrpc_deadline) - - self.assertEqual(6, len(client_event.batch_operations)) - found_client_op_types = set() - for client_result in client_event.batch_operations: - # we expect each op type to be unique - self.assertNotIn(client_result.type, found_client_op_types) - found_client_op_types.add(client_result.type) - if client_result.type == cygrpc.OperationType.receive_initial_metadata: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.received_metadata)) - elif client_result.type == cygrpc.OperationType.receive_message: - self.assertEqual(RESPONSE, client_result.received_message.bytes()) - elif client_result.type == cygrpc.OperationType.receive_status_on_client: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.received_metadata)) - self.assertEqual(SERVER_STATUS_DETAILS, - client_result.received_status_details) - self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) - self.assertEqual(set([ - cygrpc.OperationType.send_initial_metadata, - cygrpc.OperationType.send_message, - cygrpc.OperationType.send_close_from_client, - cygrpc.OperationType.receive_initial_metadata, - cygrpc.OperationType.receive_message, - cygrpc.OperationType.receive_status_on_client - ]), found_client_op_types) - - self.assertEqual(5, len(server_event.batch_operations)) - found_server_op_types = set() - for server_result in server_event.batch_operations: - self.assertNotIn(client_result.type, found_server_op_types) - found_server_op_types.add(server_result.type) - if server_result.type == cygrpc.OperationType.receive_message: - self.assertEqual(REQUEST, server_result.received_message.bytes()) - elif server_result.type == cygrpc.OperationType.receive_close_on_server: - self.assertFalse(server_result.received_cancelled) - self.assertEqual(set([ - cygrpc.OperationType.send_initial_metadata, - cygrpc.OperationType.receive_message, - cygrpc.OperationType.send_message, - cygrpc.OperationType.receive_close_on_server, - cygrpc.OperationType.send_status_from_server - ]), found_server_op_types) - - del client_call - del server_call - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/_cython/test_utilities.py b/src/python/grpcio/grpc/_cython/test_utilities.py deleted file mode 100644 index 21ea3075b4..0000000000 --- a/src/python/grpcio/grpc/_cython/test_utilities.py +++ /dev/null @@ -1,46 +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. - -import threading - -from grpc._cython._cygrpc import completion_queue - - -class CompletionQueuePollFuture: - - def __init__(self, completion_queue, deadline): - def poller_function(): - self._event_result = completion_queue.poll(deadline) - self._event_result = None - self._thread = threading.Thread(target=poller_function) - self._thread.start() - - def result(self): - self._thread.join() - return self._event_result diff --git a/src/python/grpcio/grpc/_junkdrawer/__init__.py b/src/python/grpcio/grpc/_junkdrawer/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/grpcio/grpc/_junkdrawer/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/grpcio/grpc/_junkdrawer/math_pb2.py b/src/python/grpcio/grpc/_junkdrawer/math_pb2.py deleted file mode 100644 index 20165955b4..0000000000 --- a/src/python/grpcio/grpc/_junkdrawer/math_pb2.py +++ /dev/null @@ -1,266 +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. - -# TODO(nathaniel): Remove this from source control after having made -# generation from the math.proto source part of GRPC's build-and-test -# process. - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: math.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='math.proto', - package='math', - serialized_pb=_b('\n\nmath.proto\x12\x04math\",\n\x07\x44ivArgs\x12\x10\n\x08\x64ividend\x18\x01 \x02(\x03\x12\x0f\n\x07\x64ivisor\x18\x02 \x02(\x03\"/\n\x08\x44ivReply\x12\x10\n\x08quotient\x18\x01 \x02(\x03\x12\x11\n\tremainder\x18\x02 \x02(\x03\"\x18\n\x07\x46ibArgs\x12\r\n\x05limit\x18\x01 \x01(\x03\"\x12\n\x03Num\x12\x0b\n\x03num\x18\x01 \x02(\x03\"\x19\n\x08\x46ibReply\x12\r\n\x05\x63ount\x18\x01 \x02(\x03\x32\xa4\x01\n\x04Math\x12&\n\x03\x44iv\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00\x12.\n\x07\x44ivMany\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00(\x01\x30\x01\x12#\n\x03\x46ib\x12\r.math.FibArgs\x1a\t.math.Num\"\x00\x30\x01\x12\x1f\n\x03Sum\x12\t.math.Num\x1a\t.math.Num\"\x00(\x01') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_DIVARGS = _descriptor.Descriptor( - name='DivArgs', - full_name='math.DivArgs', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='dividend', full_name='math.DivArgs.dividend', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='divisor', full_name='math.DivArgs.divisor', index=1, - number=2, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=20, - serialized_end=64, -) - - -_DIVREPLY = _descriptor.Descriptor( - name='DivReply', - full_name='math.DivReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='quotient', full_name='math.DivReply.quotient', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='remainder', full_name='math.DivReply.remainder', index=1, - number=2, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=66, - serialized_end=113, -) - - -_FIBARGS = _descriptor.Descriptor( - name='FibArgs', - full_name='math.FibArgs', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='limit', full_name='math.FibArgs.limit', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=115, - serialized_end=139, -) - - -_NUM = _descriptor.Descriptor( - name='Num', - full_name='math.Num', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='num', full_name='math.Num.num', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=141, - serialized_end=159, -) - - -_FIBREPLY = _descriptor.Descriptor( - name='FibReply', - full_name='math.FibReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='count', full_name='math.FibReply.count', index=0, - number=1, type=3, cpp_type=2, label=2, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=161, - serialized_end=186, -) - -DESCRIPTOR.message_types_by_name['DivArgs'] = _DIVARGS -DESCRIPTOR.message_types_by_name['DivReply'] = _DIVREPLY -DESCRIPTOR.message_types_by_name['FibArgs'] = _FIBARGS -DESCRIPTOR.message_types_by_name['Num'] = _NUM -DESCRIPTOR.message_types_by_name['FibReply'] = _FIBREPLY - -DivArgs = _reflection.GeneratedProtocolMessageType('DivArgs', (_message.Message,), dict( - DESCRIPTOR = _DIVARGS, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.DivArgs) - )) -_sym_db.RegisterMessage(DivArgs) - -DivReply = _reflection.GeneratedProtocolMessageType('DivReply', (_message.Message,), dict( - DESCRIPTOR = _DIVREPLY, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.DivReply) - )) -_sym_db.RegisterMessage(DivReply) - -FibArgs = _reflection.GeneratedProtocolMessageType('FibArgs', (_message.Message,), dict( - DESCRIPTOR = _FIBARGS, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.FibArgs) - )) -_sym_db.RegisterMessage(FibArgs) - -Num = _reflection.GeneratedProtocolMessageType('Num', (_message.Message,), dict( - DESCRIPTOR = _NUM, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.Num) - )) -_sym_db.RegisterMessage(Num) - -FibReply = _reflection.GeneratedProtocolMessageType('FibReply', (_message.Message,), dict( - DESCRIPTOR = _FIBREPLY, - __module__ = 'math_pb2' - # @@protoc_insertion_point(class_scope:math.FibReply) - )) -_sym_db.RegisterMessage(FibReply) - - -# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py b/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py deleted file mode 100644 index eef18f82d6..0000000000 --- a/src/python/grpcio/grpc/_junkdrawer/stock_pb2.py +++ /dev/null @@ -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. - -# TODO(nathaniel): Remove this from source control after having made -# generation from the stock.proto source part of GRPC's build-and-test -# process. - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: stock.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='stock.proto', - package='stock', - serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') -) -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - - -_STOCKREQUEST = _descriptor.Descriptor( - name='StockRequest', - full_name='stock.StockRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='symbol', full_name='stock.StockRequest.symbol', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=True, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=22, - serialized_end=84, -) - - -_STOCKREPLY = _descriptor.Descriptor( - name='StockReply', - full_name='stock.StockReply', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='price', full_name='stock.StockReply.price', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - _descriptor.FieldDescriptor( - name='symbol', full_name='stock.StockReply.symbol', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - extension_ranges=[], - oneofs=[ - ], - serialized_start=86, - serialized_end=129, -) - -DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST -DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY - -StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( - DESCRIPTOR = _STOCKREQUEST, - __module__ = 'stock_pb2' - # @@protoc_insertion_point(class_scope:stock.StockRequest) - )) -_sym_db.RegisterMessage(StockRequest) - -StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( - DESCRIPTOR = _STOCKREPLY, - __module__ = 'stock_pb2' - # @@protoc_insertion_point(class_scope:stock.StockReply) - )) -_sym_db.RegisterMessage(StockReply) - - -# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py b/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py deleted file mode 100644 index 3d629f4387..0000000000 --- a/src/python/grpcio/grpc/_links/_lonely_invocation_link_test.py +++ /dev/null @@ -1,88 +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. - -"""A test of invocation-side code unconnected to an RPC server.""" - -import unittest - -from grpc._adapter import _intermediary_low -from grpc._links import invocation -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_cases -from grpc.framework.interfaces.links import test_utilities - -_NULL_BEHAVIOR = lambda unused_argument: None - - -class LonelyInvocationLinkTest(unittest.TestCase): - - def testUpAndDown(self): - channel = _intermediary_low.Channel('nonexistent:54321', None) - invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {}) - - invocation_link.start() - invocation_link.stop() - - def _test_lonely_invocation_with_termination(self, termination): - test_operation_id = object() - test_group = 'test package.Test Service' - test_method = 'test method' - invocation_link_mate = test_utilities.RecordingLink() - - channel = _intermediary_low.Channel('nonexistent:54321', None) - invocation_link = invocation.invocation_link( - channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, - {(test_group, test_method): _NULL_BEHAVIOR}) - invocation_link.join_link(invocation_link_mate) - invocation_link.start() - - ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, - None, None, None, None, termination) - invocation_link.accept_ticket(ticket) - invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - - self.assertIsNot( - invocation_link_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - - def testLonelyInvocationLinkWithCommencementTicket(self): - self._test_lonely_invocation_with_termination(None) - - def testLonelyInvocationLinkWithEntireTicket(self): - self._test_lonely_invocation_with_termination( - links.Ticket.Termination.COMPLETION) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/python/grpcio/grpc/_links/_proto_scenarios.py b/src/python/grpcio/grpc/_links/_proto_scenarios.py deleted file mode 100644 index 320c0e0f50..0000000000 --- a/src/python/grpcio/grpc/_links/_proto_scenarios.py +++ /dev/null @@ -1,261 +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. - -"""Test scenarios using protocol buffers.""" - -import abc -import threading - -from grpc._junkdrawer import math_pb2 -from grpc.framework.common import test_constants - - -class ProtoScenario(object): - """An RPC test scenario using protocol buffers.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def group_and_method(self): - """Access the test group and method. - - Returns: - The test group and method as a pair. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize a request protocol buffer. - - Args: - request: A request protocol buffer. - - Returns: - The bytestring serialization of the given request protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, request_bytestring): - """Deserialize a request protocol buffer. - - Args: - request_bytestring: The bytestring serialization of a request protocol - buffer. - - Returns: - The request protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize a response protocol buffer. - - Args: - response: A response protocol buffer. - - Returns: - The bytestring serialization of the given response protocol buffer. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, response_bytestring): - """Deserialize a response protocol buffer. - - Args: - response_bytestring: The bytestring serialization of a response protocol - buffer. - - Returns: - The response protocol buffer deserialized from the given byte string. - """ - raise NotImplementedError() - - @abc.abstractmethod - def requests(self): - """Access the sequence of requests for this scenario. - - Returns: - A sequence of request protocol buffers. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_for_request(self, request): - """Access the response for a particular request. - - Args: - request: A request protocol buffer. - - Returns: - The response protocol buffer appropriate for the given request. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_requests(self, experimental_requests): - """Verify the requests transmitted through the system under test. - - Args: - experimental_requests: The request protocol buffers transmitted through - the system under test. - - Returns: - True if the requests satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify_responses(self, experimental_responses): - """Verify the responses transmitted through the system under test. - - Args: - experimental_responses: The response protocol buffers transmitted through - the system under test. - - Returns: - True if the responses satisfy this test scenario; False otherwise. - """ - raise NotImplementedError() - - -class EmptyScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - def group_and_method(self): - return 'math.Math', 'DivMany' - - def serialize_request(self, request): - raise ValueError('This should not be necessary to call!') - - def deserialize_request(self, request_bytestring): - raise ValueError('This should not be necessary to call!') - - def serialize_response(self, response): - raise ValueError('This should not be necessary to call!') - - def deserialize_response(self, response_bytestring): - raise ValueError('This should not be necessary to call!') - - def requests(self): - return () - - def response_for_request(self, request): - raise ValueError('This should not be necessary to call!') - - def verify_requests(self, experimental_requests): - return not experimental_requests - - def verify_responses(self, experimental_responses): - return not experimental_responses - - -class BidirectionallyUnaryScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _DIVIDEND = 59 - _DIVISOR = 7 - _QUOTIENT = 8 - _REMAINDER = 3 - - _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) - _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) - - def group_and_method(self): - return 'math.Math', 'Div' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return [self._REQUEST] - - def response_for_request(self, request): - return self._RESPONSE - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == (self._REQUEST,) - - def verify_responses(self, experimental_responses): - return tuple(experimental_responses) == (self._RESPONSE,) - - -class BidirectionallyStreamingScenario(ProtoScenario): - """A scenario that transmits no protocol buffers in either direction.""" - - _REQUESTS = tuple( - math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) - for index in range(test_constants.STREAM_LENGTH)) - - def __init__(self): - self._lock = threading.Lock() - self._responses = [] - - def group_and_method(self): - return 'math.Math', 'DivMany' - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, request_bytestring): - return math_pb2.DivArgs.FromString(request_bytestring) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, response_bytestring): - return math_pb2.DivReply.FromString(response_bytestring) - - def requests(self): - return self._REQUESTS - - def response_for_request(self, request): - quotient, remainder = divmod(request.dividend, request.divisor) - response = math_pb2.DivReply(quotient=quotient, remainder=remainder) - with self._lock: - self._responses.append(response) - return response - - def verify_requests(self, experimental_requests): - return tuple(experimental_requests) == self._REQUESTS - - def verify_responses(self, experimental_responses): - with self._lock: - return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio/grpc/_links/_transmission_test.py b/src/python/grpcio/grpc/_links/_transmission_test.py deleted file mode 100644 index 3eeec03f46..0000000000 --- a/src/python/grpcio/grpc/_links/_transmission_test.py +++ /dev/null @@ -1,231 +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. - -"""Tests transmission of tickets across gRPC-on-the-wire.""" - -import unittest - -from grpc._adapter import _intermediary_low -from grpc._links import _proto_scenarios -from grpc._links import invocation -from grpc._links import service -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_cases -from grpc.framework.interfaces.links import test_utilities - -_IDENTITY = lambda x: x - - -class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): - - def create_transmitting_links(self): - service_link = service.service_link( - {self.group_and_method(): self.deserialize_request}, - {self.group_and_method(): self.serialize_response}) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', - {self.group_and_method(): self.serialize_request}, - {self.group_and_method(): self.deserialize_response}) - invocation_link.start() - return invocation_link, service_link - - def destroy_transmitting_links(self, invocation_side_link, service_side_link): - invocation_side_link.stop() - service_side_link.stop_gracefully() - - def create_invocation_initial_metadata(self): - return ( - ('first invocation initial metadata key', 'just a string value'), - ('second invocation initial metadata key', '0123456789'), - ('third invocation initial metadata key-bin', '\x00\x57' * 100), - ) - - def create_invocation_terminal_metadata(self): - return None - - def create_service_initial_metadata(self): - return ( - ('first service initial metadata key', 'just another string value'), - ('second service initial metadata key', '9876543210'), - ('third service initial metadata key-bin', '\x00\x59\x02' * 100), - ) - - def create_service_terminal_metadata(self): - return ( - ('first service terminal metadata key', 'yet another string value'), - ('second service terminal metadata key', 'abcdefghij'), - ('third service terminal metadata key-bin', '\x00\x37' * 100), - ) - - def create_invocation_completion(self): - return None, None - - def create_service_completion(self): - return _intermediary_low.Code.OK, 'An exuberant test "details" message!' - - def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): - # we need to filter out any additional metadata added in transmitted_metadata - # since implementations are allowed to add to what is sent (in any position) - keys, _ = zip(*original_metadata) - self.assertSequenceEqual( - original_metadata, - [x for x in transmitted_metadata if x[0] in keys]) - - -class RoundTripTest(unittest.TestCase): - - def testZeroMessageRoundTrip(self): - test_operation_id = object() - test_group = 'test package.Test Group' - test_method = 'test method' - identity_transformation = {(test_group, test_method): _IDENTITY} - test_code = _intermediary_low.Code.OK - test_message = 'a test message' - - service_link = service.service_link( - identity_transformation, identity_transformation) - service_mate = test_utilities.RecordingLink() - service_link.join_link(service_mate) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', identity_transformation, identity_transformation) - invocation_mate = test_utilities.RecordingLink() - invocation_link.join_link(invocation_mate) - invocation_link.start() - - invocation_ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, - None, None, None, None, links.Ticket.Termination.COMPLETION) - invocation_link.accept_ticket(invocation_ticket) - service_mate.block_until_tickets_satisfy(test_cases.terminated) - - service_ticket = links.Ticket( - service_mate.tickets()[-1].operation_id, 0, None, None, None, None, - None, None, None, None, test_code, test_message, - links.Ticket.Termination.COMPLETION) - service_link.accept_ticket(service_ticket) - invocation_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - service_link.stop_gracefully() - - self.assertIs( - service_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - self.assertIs( - invocation_mate.tickets()[-1].termination, - links.Ticket.Termination.COMPLETION) - - def _perform_scenario_test(self, scenario): - test_operation_id = object() - test_group, test_method = scenario.group_and_method() - test_code = _intermediary_low.Code.OK - test_message = 'a scenario test message' - - service_link = service.service_link( - {(test_group, test_method): scenario.deserialize_request}, - {(test_group, test_method): scenario.serialize_response}) - service_mate = test_utilities.RecordingLink() - service_link.join_link(service_mate) - port = service_link.add_port(0, None) - service_link.start() - channel = _intermediary_low.Channel('localhost:%d' % port, None) - invocation_link = invocation.invocation_link( - channel, 'localhost', - {(test_group, test_method): scenario.serialize_request}, - {(test_group, test_method): scenario.deserialize_response}) - invocation_mate = test_utilities.RecordingLink() - invocation_link.join_link(invocation_mate) - invocation_link.start() - - invocation_ticket = links.Ticket( - test_operation_id, 0, test_group, test_method, - links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, - None, None, None, None, None) - invocation_link.accept_ticket(invocation_ticket) - requests = scenario.requests() - for request_index, request in enumerate(requests): - request_ticket = links.Ticket( - test_operation_id, 1 + request_index, None, None, None, None, 1, None, - request, None, None, None, None) - invocation_link.accept_ticket(request_ticket) - service_mate.block_until_tickets_satisfy( - test_cases.at_least_n_payloads_received_predicate(1 + request_index)) - response_ticket = links.Ticket( - service_mate.tickets()[0].operation_id, request_index, None, None, - None, None, 1, None, scenario.response_for_request(request), None, - None, None, None) - service_link.accept_ticket(response_ticket) - invocation_mate.block_until_tickets_satisfy( - test_cases.at_least_n_payloads_received_predicate(1 + request_index)) - request_count = len(requests) - invocation_completion_ticket = links.Ticket( - test_operation_id, request_count + 1, None, None, None, None, None, - None, None, None, None, None, links.Ticket.Termination.COMPLETION) - invocation_link.accept_ticket(invocation_completion_ticket) - service_mate.block_until_tickets_satisfy(test_cases.terminated) - service_completion_ticket = links.Ticket( - service_mate.tickets()[0].operation_id, request_count, None, None, None, - None, None, None, None, None, test_code, test_message, - links.Ticket.Termination.COMPLETION) - service_link.accept_ticket(service_completion_ticket) - invocation_mate.block_until_tickets_satisfy(test_cases.terminated) - - invocation_link.stop() - service_link.stop_gracefully() - - observed_requests = tuple( - ticket.payload for ticket in service_mate.tickets() - if ticket.payload is not None) - observed_responses = tuple( - ticket.payload for ticket in invocation_mate.tickets() - if ticket.payload is not None) - self.assertTrue(scenario.verify_requests(observed_requests)) - self.assertTrue(scenario.verify_responses(observed_responses)) - - def testEmptyScenario(self): - self._perform_scenario_test(_proto_scenarios.EmptyScenario()) - - def testBidirectionallyUnaryScenario(self): - self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) - - def testBidirectionallyStreamingScenario(self): - self._perform_scenario_test( - _proto_scenarios.BidirectionallyStreamingScenario()) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/early_adopter/implementations_test.py b/src/python/grpcio/grpc/early_adopter/implementations_test.py deleted file mode 100644 index 49f0e949c4..0000000000 --- a/src/python/grpcio/grpc/early_adopter/implementations_test.py +++ /dev/null @@ -1,180 +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. - -# TODO(nathaniel): Expand this test coverage. - -"""Test of the GRPC-backed ForeLink and RearLink.""" - -import unittest - -from grpc.early_adopter import implementations -from grpc.framework.alpha import utilities -from grpc._junkdrawer import math_pb2 - -SERVICE_NAME = 'math.Math' - -DIV = 'Div' -DIV_MANY = 'DivMany' -FIB = 'Fib' -SUM = 'Sum' - -def _fibbonacci(limit): - left, right = 0, 1 - for _ in xrange(limit): - yield left - left, right = right, left + right - - -def _div(request, unused_context): - return math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _div_many(request_iterator, unused_context): - for request in request_iterator: - yield math_pb2.DivReply( - quotient=request.dividend / request.divisor, - remainder=request.dividend % request.divisor) - - -def _fib(request, unused_context): - for number in _fibbonacci(request.limit): - yield math_pb2.Num(num=number) - - -def _sum(request_iterator, unused_context): - accumulation = 0 - for request in request_iterator: - accumulation += request.num - return math_pb2.Num(num=accumulation) - - -_INVOCATION_DESCRIPTIONS = { - DIV: utilities.unary_unary_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - DIV_MANY: utilities.stream_stream_invocation_description( - math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), - FIB: utilities.unary_stream_invocation_description( - math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), - SUM: utilities.stream_unary_invocation_description( - math_pb2.Num.SerializeToString, math_pb2.Num.FromString), -} - -_SERVICE_DESCRIPTIONS = { - DIV: utilities.unary_unary_service_description( - _div, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - DIV_MANY: utilities.stream_stream_service_description( - _div_many, math_pb2.DivArgs.FromString, - math_pb2.DivReply.SerializeToString), - FIB: utilities.unary_stream_service_description( - _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), - SUM: utilities.stream_unary_service_description( - _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), -} - -_TIMEOUT = 3 - - -class EarlyAdopterImplementationsTest(unittest.TestCase): - - def setUp(self): - self.server = implementations.server( - SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) - self.server.start() - port = self.server.port() - self.stub = implementations.stub( - SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) - - def tearDown(self): - self.server.stop() - - def testUpAndDown(self): - with self.stub: - pass - - def testUnaryUnary(self): - divisor = 59 - dividend = 973 - expected_quotient = dividend / divisor - expected_remainder = dividend % divisor - - with self.stub: - response = self.stub.Div( - math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) - self.assertEqual(expected_quotient, response.quotient) - self.assertEqual(expected_remainder, response.remainder) - - def testUnaryStream(self): - stream_length = 43 - - with self.stub: - response_iterator = self.stub.Fib( - math_pb2.FibArgs(limit=stream_length), _TIMEOUT) - numbers = tuple(response.num for response in response_iterator) - for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): - self.assertEqual(early + middle, later) - self.assertEqual(stream_length, len(numbers)) - - def testStreamUnary(self): - stream_length = 127 - - with self.stub: - response_future = self.stub.Sum.async( - (math_pb2.Num(num=index) for index in range(stream_length)), - _TIMEOUT) - self.assertEqual( - (stream_length * (stream_length - 1)) / 2, - response_future.result().num) - - def testStreamStream(self): - stream_length = 179 - divisor_offset = 71 - dividend_offset = 1763 - - with self.stub: - response_iterator = self.stub.DivMany( - (math_pb2.DivArgs( - divisor=divisor_offset + index, - dividend=dividend_offset + index) - for index in range(stream_length)), - _TIMEOUT) - for index, response in enumerate(response_iterator): - self.assertEqual( - (dividend_offset + index) / (divisor_offset + index), - response.quotient) - self.assertEqual( - (dividend_offset + index) % (divisor_offset + index), - response.remainder) - self.assertEqual(stream_length, index + 1) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/base/implementations_test.py b/src/python/grpcio/grpc/framework/base/implementations_test.py deleted file mode 100644 index 72087f4456..0000000000 --- a/src/python/grpcio/grpc/framework/base/implementations_test.py +++ /dev/null @@ -1,80 +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. - -"""Tests for grpc.framework.base.implementations.""" - -import unittest - -from grpc.framework.base import implementations -from grpc.framework.base import interfaces_test_case -from grpc.framework.base import util -from grpc.framework.foundation import logging_pool - -POOL_MAX_WORKERS = 10 -DEFAULT_TIMEOUT = 30 -MAXIMUM_TIMEOUT = 60 - - -class ImplementationsTest( - interfaces_test_case.FrontAndBackTest, unittest.TestCase): - - def setUp(self): - self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) - self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) - self.front = implementations.front_link( - self.front_work_pool, self.front_transmission_pool, - self.front_utility_pool) - self.back = implementations.back_link( - self.test_servicer, self.back_work_pool, self.back_transmission_pool, - self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) - self.front.join_rear_link(self.back) - self.back.join_fore_link(self.front) - - def tearDown(self): - util.wait_for_idle(self.back) - util.wait_for_idle(self.front) - self.memory_transmission_pool.shutdown(wait=True) - self.front_work_pool.shutdown(wait=True) - self.front_transmission_pool.shutdown(wait=True) - self.front_utility_pool.shutdown(wait=True) - self.back_work_pool.shutdown(wait=True) - self.back_transmission_pool.shutdown(wait=True) - self.back_utility_pool.shutdown(wait=True) - self.test_pool.shutdown(wait=True) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/base/interfaces_test_case.py b/src/python/grpcio/grpc/framework/base/interfaces_test_case.py deleted file mode 100644 index dec10c2924..0000000000 --- a/src/python/grpcio/grpc/framework/base/interfaces_test_case.py +++ /dev/null @@ -1,307 +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. - -"""Abstract tests against the interfaces of the base layer of RPC Framework.""" - -import threading -import time - -from grpc.framework.base import interfaces -from grpc.framework.base import util -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_testing -from grpc.framework.foundation import stream_util - -TICK = 0.1 -SMALL_TIMEOUT = TICK * 50 -STREAM_LENGTH = 100 - -SYNCHRONOUS_ECHO = 'synchronous echo' -ASYNCHRONOUS_ECHO = 'asynchronous echo' -IMMEDIATE_FAILURE = 'immediate failure' -TRIGGERED_FAILURE = 'triggered failure' -WAIT_ON_CONDITION = 'wait on condition' - -EMPTY_OUTCOME_DICT = { - interfaces.Outcome.COMPLETED: 0, - interfaces.Outcome.CANCELLED: 0, - interfaces.Outcome.EXPIRED: 0, - interfaces.Outcome.RECEPTION_FAILURE: 0, - interfaces.Outcome.TRANSMISSION_FAILURE: 0, - interfaces.Outcome.SERVICER_FAILURE: 0, - interfaces.Outcome.SERVICED_FAILURE: 0, - } - - -def _synchronous_echo(output_consumer): - return stream_util.TransformingConsumer(lambda x: x, output_consumer) - - -class AsynchronousEcho(stream.Consumer): - """A stream.Consumer that echoes its input to another stream.Consumer.""" - - def __init__(self, output_consumer, pool): - self._lock = threading.Lock() - self._output_consumer = output_consumer - self._pool = pool - - self._queue = [] - self._spinning = False - - def _spin(self, value, complete): - while True: - if value: - if complete: - self._output_consumer.consume_and_terminate(value) - else: - self._output_consumer.consume(value) - elif complete: - self._output_consumer.terminate() - with self._lock: - if self._queue: - value, complete = self._queue.pop(0) - else: - self._spinning = False - return - - def consume(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, False)) - else: - self._spinning = True - self._pool.submit(self._spin, value, False) - - def terminate(self): - with self._lock: - if self._spinning: - self._queue.append((None, True)) - else: - self._spinning = True - self._pool.submit(self._spin, None, True) - - def consume_and_terminate(self, value): - with self._lock: - if self._spinning: - self._queue.append((value, True)) - else: - self._spinning = True - self._pool.submit(self._spin, value, True) - - -class TestServicer(interfaces.Servicer): - """An interfaces.Servicer with instrumented for testing.""" - - def __init__(self, pool): - self._pool = pool - self.condition = threading.Condition() - self._released = False - - def service(self, name, context, output_consumer): - if name == SYNCHRONOUS_ECHO: - return _synchronous_echo(output_consumer) - elif name == ASYNCHRONOUS_ECHO: - return AsynchronousEcho(output_consumer, self._pool) - elif name == IMMEDIATE_FAILURE: - raise ValueError() - elif name == TRIGGERED_FAILURE: - raise NotImplementedError - elif name == WAIT_ON_CONDITION: - with self.condition: - while not self._released: - self.condition.wait() - return _synchronous_echo(output_consumer) - else: - raise NotImplementedError() - - def release(self): - with self.condition: - self._released = True - self.condition.notify_all() - - -class EasyServicedIngestor(interfaces.ServicedIngestor): - """A trivial implementation of interfaces.ServicedIngestor.""" - - def __init__(self, consumer): - self._consumer = consumer - - def consumer(self, operation_context): - """See interfaces.ServicedIngestor.consumer for specification.""" - return self._consumer - - -class FrontAndBackTest(object): - """A test suite usable against any joined Front and Back.""" - - # Pylint doesn't know that this is a unittest.TestCase mix-in. - # pylint: disable=invalid-name - - def testSimplestCall(self): - """Tests the absolute simplest call - a one-ticket fire-and-forget.""" - self.front.operate( - SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, - util.none_serviced_subscription(), 'test trace ID') - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) The ticket is still either in the front waiting to be transmitted - # or is somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the ticket bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) The ticket arrived at the back and the back completed the operation. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.COMPLETED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - # It's true that if the ticket had arrived at the back and the back had - # begun processing that wait_for_idle could hold test execution until the - # back completed the operation, but that doesn't really collapse the - # possibility space down to one solution. - - def testEntireEcho(self): - """Tests a very simple one-ticket-each-way round-trip.""" - test_payload = 'test payload' - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - self.front.operate( - ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, - 'test trace ID') - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual([(test_payload, True)], test_consumer.calls) - - def testBidirectionalStreamingEcho(self): - """Tests sending multiple tickets each way.""" - test_payload_template = 'test_payload: %03d' - test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - - for test_payload in test_payloads: - operation.consumer.consume(test_payload) - operation.consumer.terminate() - - util.wait_for_idle(self.front) - util.wait_for_idle(self.back) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertEqual( - 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) - self.assertListEqual(test_payloads, test_consumer.values()) - - def testCancellation(self): - """Tests cancelling a long-lived operation.""" - test_consumer = stream_testing.TestConsumer() - subscription = util.full_serviced_subscription( - EasyServicedIngestor(test_consumer)) - - operation = self.front.operate( - ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, - 'test trace ID') - operation.cancel() - - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) - util.wait_for_idle(self.back) - self.assertListEqual([], test_consumer.calls) - - # Assuming nothing really pathological (such as pauses on the order of - # SMALL_TIMEOUT interfering with this test) there are a two different ways - # the back could have experienced execution up to this point: - # (1) Both tickets are still either in the front waiting to be transmitted - # or are somewhere on the link between the front and the back. The back has - # no idea that this test is even happening. Calling wait_for_idle on it - # would do no good because in this case the back is idle and the call would - # return with the tickets bound for it still in the front or on the link. - back_operation_stats = self.back.operation_stats() - first_back_possibility = EMPTY_OUTCOME_DICT - # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. - # The back started processing based on the first ticket and then stopped - # upon receiving the cancellation ticket. - second_back_possibility = dict(EMPTY_OUTCOME_DICT) - second_back_possibility[interfaces.Outcome.CANCELLED] = 1 - self.assertIn( - back_operation_stats, (first_back_possibility, second_back_possibility)) - - def testExpiration(self): - """Tests that operations time out.""" - timeout = TICK * 2 - allowance = TICK # How much extra time to - condition = threading.Condition() - test_payload = 'test payload' - subscription = util.termination_only_serviced_subscription() - start_time = time.time() - - outcome_cell = [None] - termination_time_cell = [None] - def termination_action(outcome): - with condition: - outcome_cell[0] = outcome - termination_time_cell[0] = time.time() - condition.notify() - - with condition: - operation = self.front.operate( - SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, - 'test trace ID') - operation.context.add_termination_callback(termination_action) - while outcome_cell[0] is None: - condition.wait() - - duration = termination_time_cell[0] - start_time - self.assertLessEqual(timeout, duration) - self.assertLess(duration, timeout + allowance) - self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) - util.wait_for_idle(self.front) - self.assertEqual( - 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) - util.wait_for_idle(self.back) - self.assertLessEqual( - 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/grpcio/grpc/framework/common/test_constants.py b/src/python/grpcio/grpc/framework/common/test_constants.py deleted file mode 100644 index 3126d0d82c..0000000000 --- a/src/python/grpcio/grpc/framework/common/test_constants.py +++ /dev/null @@ -1,43 +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. - -"""Constants shared among tests throughout RPC Framework.""" - -# Value for maximum duration in seconds of RPCs that may time out as part of a -# test. -SHORT_TIMEOUT = 4 -# Absurdly large value for maximum duration in seconds for should-not-time-out -# RPCs made during tests. -LONG_TIMEOUT = 3000 - -# The number of payloads to transmit in streaming tests. -STREAM_LENGTH = 200 - -# The size of thread pools to use in tests. -POOL_SIZE = 10 diff --git a/src/python/grpcio/grpc/framework/common/test_control.py b/src/python/grpcio/grpc/framework/common/test_control.py deleted file mode 100644 index 3960c4e649..0000000000 --- a/src/python/grpcio/grpc/framework/common/test_control.py +++ /dev/null @@ -1,87 +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. - -"""Code for instructing systems under test to block or fail.""" - -import abc -import contextlib -import threading - - -class Control(object): - """An object that accepts program control from a system under test. - - Systems under test passed a Control should call its control() method - frequently during execution. The control() method may block, raise an - exception, or do nothing, all according to the enclosing test's desire for - the system under test to simulate hanging, failing, or functioning. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def control(self): - """Potentially does anything.""" - raise NotImplementedError() - - -class PauseFailControl(Control): - """A Control that can be used to pause or fail code under control.""" - - def __init__(self): - self._condition = threading.Condition() - self._paused = False - self._fail = False - - def control(self): - with self._condition: - if self._fail: - raise ValueError() - - while self._paused: - self._condition.wait() - - @contextlib.contextmanager - def pause(self): - """Pauses code under control while controlling code is in context.""" - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): - """Fails code under control while controlling code is in context.""" - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False diff --git a/src/python/grpcio/grpc/framework/common/test_coverage.py b/src/python/grpcio/grpc/framework/common/test_coverage.py deleted file mode 100644 index a7ed3582c4..0000000000 --- a/src/python/grpcio/grpc/framework/common/test_coverage.py +++ /dev/null @@ -1,116 +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. - -"""Governs coverage for tests of RPCs throughout RPC Framework.""" - -import abc - -# This code is designed for use with the unittest module. -# pylint: disable=invalid-name - - -class Coverage(object): - """Specification of test coverage.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testSuccessfulUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSequentialInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/_test_case.py b/src/python/grpcio/grpc/framework/face/_test_case.py deleted file mode 100644 index 642d500628..0000000000 --- a/src/python/grpcio/grpc/framework/face/_test_case.py +++ /dev/null @@ -1,61 +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. - -"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" - -from grpc.framework.face import implementations -from grpc.framework.face.testing import base_util -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_POOL_SIZE = 10 - - -class FaceTestCase(test_case.FaceTestCase): - """Provides abstract Face-layer tests an in-memory implementation.""" - - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - - servicer = implementations.servicer( - servicer_pool, method_implementations, multi_method_implementation) - - linked_pair = base_util.linked_pair(servicer, _TIMEOUT) - stub = implementations.generic_stub(linked_pair.front, stub_pool) - return stub, (servicer_pool, stub_pool, linked_pair) - - def tear_down_implementation(self, memo): - servicer_pool, stub_pool, linked_pair = memo - linked_pair.shut_down() - stub_pool.shutdown(wait=True) - servicer_pool.shutdown(wait=True) diff --git a/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py b/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py deleted file mode 100644 index 763f0f0edc..0000000000 --- a/src/python/grpcio/grpc/framework/face/blocking_invocation_inline_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import blocking_invocation_inline_service_test_case as test_case - - -class BlockingInvocationInlineServiceTest( - _test_case.FaceTestCase, - test_case.BlockingInvocationInlineServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py deleted file mode 100644 index e1ab3cf711..0000000000 --- a/src/python/grpcio/grpc/framework/face/event_invocation_synchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case - - -class EventInvocationSynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.EventInvocationSynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py deleted file mode 100644 index 2d13bb911d..0000000000 --- a/src/python/grpcio/grpc/framework/face/future_invocation_asynchronous_event_service_test.py +++ /dev/null @@ -1,46 +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. - -"""One of the tests of the Face layer of RPC Framework.""" - -import unittest - -from grpc.framework.face import _test_case -from grpc.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case - - -class FutureInvocationAsynchronousEventServiceTest( - _test_case.FaceTestCase, - test_case.FutureInvocationAsynchronousEventServiceTestCase, - unittest.TestCase): - pass - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/face/testing/__init__.py b/src/python/grpcio/grpc/framework/face/testing/__init__.py deleted file mode 100644 index 7086519106..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/__init__.py +++ /dev/null @@ -1,30 +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. - - diff --git a/src/python/grpcio/grpc/framework/face/testing/base_util.py b/src/python/grpcio/grpc/framework/face/testing/base_util.py deleted file mode 100644 index 1df1529b27..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/base_util.py +++ /dev/null @@ -1,102 +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. - -"""Utilities for creating Base-layer objects for use in Face-layer tests.""" - -import abc - -# interfaces is referenced from specification in this module. -from grpc.framework.base import util as _base_util -from grpc.framework.base import implementations -from grpc.framework.base import in_memory -from grpc.framework.base import interfaces # pylint: disable=unused-import -from grpc.framework.foundation import logging_pool - -_POOL_SIZE_LIMIT = 5 - -_MAXIMUM_TIMEOUT = 90 - - -class LinkedPair(object): - """A Front and Back that are linked to one another. - - Attributes: - front: An interfaces.Front. - back: An interfaces.Back. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def shut_down(self): - """Shuts down this object and releases its resources.""" - raise NotImplementedError() - - -class _LinkedPair(LinkedPair): - - def __init__(self, front, back, pools): - self.front = front - self.back = back - self._pools = pools - - def shut_down(self): - _base_util.wait_for_idle(self.front) - _base_util.wait_for_idle(self.back) - - for pool in self._pools: - pool.shutdown(wait=True) - - -def linked_pair(servicer, default_timeout): - """Creates a Server and Stub linked together for use.""" - link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) - pools = ( - link_pool, - front_work_pool, front_transmission_pool, front_utility_pool, - back_work_pool, back_transmission_pool, back_utility_pool) - - link = in_memory.Link(link_pool) - front = implementations.front_link( - front_work_pool, front_transmission_pool, front_utility_pool) - back = implementations.back_link( - servicer, back_work_pool, back_transmission_pool, back_utility_pool, - default_timeout, _MAXIMUM_TIMEOUT) - front.join_rear_link(link) - link.join_fore_link(front) - back.join_fore_link(link) - link.join_rear_link(back) - - return _LinkedPair(front, back, pools) diff --git a/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py deleted file mode 100644 index e57ee00104..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/blocking_invocation_inline_service_test_case.py +++ /dev/null @@ -1,222 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -# unittest is referenced from specification in this module. -import abc -import unittest # pylint: disable=unused-import - -from grpc.framework.face import exceptions -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case - -_TIMEOUT = 3 -_LONG_TIMEOUT = 45 - - -class BlockingInvocationInlineServiceTestCase( - test_case.FaceTestCase, coverage.BlockingCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, None) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.inline_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response = self.stub.blocking_value_in_value_out( - name, request, _LONG_TIMEOUT) - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response = self.stub.blocking_stream_in_value_out( - name, iter(requests), _LONG_TIMEOUT) - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response = self.stub.blocking_value_in_value_out( - name, first_request, _TIMEOUT) - - test_messages.verify(first_request, first_response, self) - - second_response = self.stub.blocking_value_in_value_out( - name, second_request, _TIMEOUT) - - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - multi_callable = self.stub.unary_unary_multi_callable(name) - multi_callable(request, _TIMEOUT) - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - multi_callable = self.stub.stream_unary_multi_callable(name) - multi_callable(iter(requests), _TIMEOUT) - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(), self.assertRaises(exceptions.ServicerError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) diff --git a/src/python/grpcio/grpc/framework/face/testing/callback.py b/src/python/grpcio/grpc/framework/face/testing/callback.py deleted file mode 100644 index d0e63c8c56..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/callback.py +++ /dev/null @@ -1,94 +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. - -"""A utility useful in tests of asynchronous, event-driven interfaces.""" - -import threading - -from grpc.framework.foundation import stream - - -class Callback(stream.Consumer): - """A utility object useful in tests of asynchronous code.""" - - def __init__(self): - self._condition = threading.Condition() - self._unary_response = None - self._streamed_responses = [] - self._completed = False - self._abortion = None - - def abort(self, abortion): - with self._condition: - self._abortion = abortion - self._condition.notify_all() - - def complete(self, unary_response): - with self._condition: - self._unary_response = unary_response - self._completed = True - self._condition.notify_all() - - def consume(self, streamed_response): - with self._condition: - self._streamed_responses.append(streamed_response) - - def terminate(self): - with self._condition: - self._completed = True - self._condition.notify_all() - - def consume_and_terminate(self, streamed_response): - with self._condition: - self._streamed_responses.append(streamed_response) - self._completed = True - self._condition.notify_all() - - def block_until_terminated(self): - with self._condition: - while self._abortion is None and not self._completed: - self._condition.wait() - - def response(self): - with self._condition: - if self._abortion is None: - return self._unary_response - else: - raise AssertionError('Aborted with abortion "%s"!' % self._abortion) - - def responses(self): - with self._condition: - if self._abortion is None: - return list(self._streamed_responses) - else: - raise AssertionError('Aborted with abortion "%s"!' % self._abortion) - - def abortion(self): - with self._condition: - return self._abortion diff --git a/src/python/grpcio/grpc/framework/face/testing/control.py b/src/python/grpcio/grpc/framework/face/testing/control.py deleted file mode 100644 index 3960c4e649..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/control.py +++ /dev/null @@ -1,87 +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. - -"""Code for instructing systems under test to block or fail.""" - -import abc -import contextlib -import threading - - -class Control(object): - """An object that accepts program control from a system under test. - - Systems under test passed a Control should call its control() method - frequently during execution. The control() method may block, raise an - exception, or do nothing, all according to the enclosing test's desire for - the system under test to simulate hanging, failing, or functioning. - """ - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def control(self): - """Potentially does anything.""" - raise NotImplementedError() - - -class PauseFailControl(Control): - """A Control that can be used to pause or fail code under control.""" - - def __init__(self): - self._condition = threading.Condition() - self._paused = False - self._fail = False - - def control(self): - with self._condition: - if self._fail: - raise ValueError() - - while self._paused: - self._condition.wait() - - @contextlib.contextmanager - def pause(self): - """Pauses code under control while controlling code is in context.""" - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - @contextlib.contextmanager - def fail(self): - """Fails code under control while controlling code is in context.""" - with self._condition: - self._fail = True - yield - with self._condition: - self._fail = False diff --git a/src/python/grpcio/grpc/framework/face/testing/coverage.py b/src/python/grpcio/grpc/framework/face/testing/coverage.py deleted file mode 100644 index f3aca113fe..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/coverage.py +++ /dev/null @@ -1,123 +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. - -"""Governs coverage for the tests of the Face layer of RPC Framework.""" - -import abc - -# These classes are only valid when inherited by unittest.TestCases. -# pylint: disable=invalid-name - - -class BlockingCoverage(object): - """Specification of test coverage for blocking behaviors.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testSuccessfulUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSuccessfulStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testSequentialInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testExpiredStreamRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testFailedStreamRequestStreamResponse(self): - raise NotImplementedError() - - -class FullCoverage(BlockingCoverage): - """Specification of test coverage for non-blocking behaviors.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def testParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @abc.abstractmethod - def testCancelledStreamRequestStreamResponse(self): - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/digest.py b/src/python/grpcio/grpc/framework/face/testing/digest.py deleted file mode 100644 index db8fcbb018..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/digest.py +++ /dev/null @@ -1,450 +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. - -"""Code for making a service.TestService more amenable to use in tests.""" - -import collections -import threading - -# testing_control, interfaces, and testing_service are referenced from -# specification in this module. -from grpc.framework.common import cardinality -from grpc.framework.common import style -from grpc.framework.face import exceptions -from grpc.framework.face import interfaces as face_interfaces -from grpc.framework.face.testing import control as testing_control # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import service as testing_service # pylint: disable=unused-import -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util - -_IDENTITY = lambda x: x - - -class TestServiceDigest( - collections.namedtuple( - 'TestServiceDigest', - ['name', - 'methods', - 'inline_method_implementations', - 'event_method_implementations', - 'multi_method_implementation', - 'unary_unary_messages_sequences', - 'unary_stream_messages_sequences', - 'stream_unary_messages_sequences', - 'stream_stream_messages_sequences'])): - """A transformation of a service.TestService. - - Attributes: - name: The RPC service name to be used in the test. - methods: A sequence of interfaces.Method objects describing the RPC - methods that will be called during the test. - inline_method_implementations: A dict from RPC method name to - face_interfaces.MethodImplementation object to be used in tests of - in-line calls to behaviors under test. - event_method_implementations: A dict from RPC method name to - face_interfaces.MethodImplementation object to be used in tests of - event-driven calls to behaviors under test. - multi_method_implementation: A face_interfaces.MultiMethodImplementation to - be used in tests of generic calls to behaviors under test. - unary_unary_messages_sequences: A dict from method name to sequence of - service.UnaryUnaryTestMessages objects to be used to test the method - with the given name. - unary_stream_messages_sequences: A dict from method name to sequence of - service.UnaryStreamTestMessages objects to be used to test the method - with the given name. - stream_unary_messages_sequences: A dict from method name to sequence of - service.StreamUnaryTestMessages objects to be used to test the method - with the given name. - stream_stream_messages_sequences: A dict from method name to sequence of - service.StreamStreamTestMessages objects to be used to test the - method with the given name. - serialization: A serial.Serialization object describing serialization - behaviors for all the RPC methods. - """ - - -class _BufferingConsumer(stream.Consumer): - """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" - - def __init__(self): - self.consumed = [] - self.terminated = False - - def consume(self, value): - self.consumed.append(value) - - def terminate(self): - self.terminated = True - - def consume_and_terminate(self, value): - self.consumed.append(value) - self.terminated = True - - -class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_unary_test_method, control): - self._test_method = unary_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.INLINE - - def unary_unary_inline(self, request, context): - response_list = [] - self._test_method.service( - request, response_list.append, context, self._control) - return response_list.pop(0) - - -class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_unary_test_method, control, pool): - self._test_method = unary_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.EVENT - - def unary_unary_event(self, request, response_callback, context): - if self._pool is None: - self._test_method.service( - request, response_callback, context, self._control) - else: - self._pool.submit( - self._test_method.service, request, response_callback, context, - self._control) - - -class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_stream_test_method, control): - self._test_method = unary_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.INLINE - - def unary_stream_inline(self, request, context): - response_consumer = _BufferingConsumer() - self._test_method.service( - request, response_consumer, context, self._control) - for response in response_consumer.consumed: - yield response - - -class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, unary_stream_test_method, control, pool): - self._test_method = unary_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.EVENT - - def unary_stream_event(self, request, response_consumer, context): - if self._pool is None: - self._test_method.service( - request, response_consumer, context, self._control) - else: - self._pool.submit( - self._test_method.service, request, response_consumer, context, - self._control) - - -class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_unary_test_method, control): - self._test_method = stream_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.INLINE - - def stream_unary_inline(self, request_iterator, context): - response_list = [] - request_consumer = self._test_method.service( - response_list.append, context, self._control) - for request in request_iterator: - request_consumer.consume(request) - request_consumer.terminate() - return response_list.pop(0) - - -class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_unary_test_method, control, pool): - self._test_method = stream_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.EVENT - - def stream_unary_event(self, response_callback, context): - request_consumer = self._test_method.service( - response_callback, context, self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_stream_test_method, control): - self._test_method = stream_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.INLINE - - def stream_stream_inline(self, request_iterator, context): - response_consumer = _BufferingConsumer() - request_consumer = self._test_method.service( - response_consumer, context, self._control) - - for request in request_iterator: - request_consumer.consume(request) - while response_consumer.consumed: - yield response_consumer.consumed.pop(0) - response_consumer.terminate() - - -class _EventStreamStreamMethod(face_interfaces.MethodImplementation): - - def __init__(self, stream_stream_test_method, control, pool): - self._test_method = stream_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.EVENT - - def stream_stream_event(self, response_consumer, context): - request_consumer = self._test_method.service( - response_consumer, context, self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _UnaryConsumer(stream.Consumer): - """A Consumer that only allows consumption of exactly one value.""" - - def __init__(self, action): - self._lock = threading.Lock() - self._action = action - self._consumed = False - self._terminated = False - - def consume(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - - self._action(value) - - def terminate(self): - with self._lock: - if not self._consumed: - raise ValueError('Unary consumer hasn\'t yet consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._terminated = True - - def consume_and_terminate(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - self._terminated = True - - self._action(value) - - -class _UnaryUnaryAdaptation(object): - - def __init__(self, unary_unary_test_method): - self._method = unary_unary_test_method - - def service(self, response_consumer, context, control): - def action(request): - self._method.service( - request, response_consumer.consume_and_terminate, context, control) - return _UnaryConsumer(action) - - -class _UnaryStreamAdaptation(object): - - def __init__(self, unary_stream_test_method): - self._method = unary_stream_test_method - - def service(self, response_consumer, context, control): - def action(request): - self._method.service(request, response_consumer, context, control) - return _UnaryConsumer(action) - - -class _StreamUnaryAdaptation(object): - - def __init__(self, stream_unary_test_method): - self._method = stream_unary_test_method - - def service(self, response_consumer, context, control): - return self._method.service( - response_consumer.consume_and_terminate, context, control) - - -class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): - - def __init__(self, methods, control, pool): - self._methods = methods - self._control = control - self._pool = pool - - def service(self, name, response_consumer, context): - method = self._methods.get(name, None) - if method is None: - raise exceptions.NoSuchMethodError(name) - elif self._pool is None: - return method(response_consumer, context, self._control) - else: - request_consumer = method(response_consumer, context, self._control) - return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) - - -class _Assembly( - collections.namedtuple( - '_Assembly', - ['methods', 'inlines', 'events', 'adaptations', 'messages'])): - """An intermediate structure created when creating a TestServiceDigest.""" - - -def _assemble( - scenarios, names, inline_method_constructor, event_method_constructor, - adapter, control, pool): - """Creates an _Assembly from the given scenarios.""" - methods = [] - inlines = {} - events = {} - adaptations = {} - messages = {} - for name, scenario in scenarios.iteritems(): - if name in names: - raise ValueError('Repeated name "%s"!' % name) - - test_method = scenario[0] - inline_method = inline_method_constructor(test_method, control) - event_method = event_method_constructor(test_method, control, pool) - adaptation = adapter(test_method) - - methods.append(test_method) - inlines[name] = inline_method - events[name] = event_method - adaptations[name] = adaptation - messages[name] = scenario[1] - - return _Assembly(methods, inlines, events, adaptations, messages) - - -def digest(service, control, pool): - """Creates a TestServiceDigest from a TestService. - - Args: - service: A testing_service.TestService. - control: A testing_control.Control. - pool: If RPC methods should be serviced in a separate thread, a thread pool. - None if RPC methods should be serviced in the thread belonging to the - run-time that calls for their service. - - Returns: - A TestServiceDigest synthesized from the given service.TestService. - """ - names = set() - - unary_unary = _assemble( - service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, - _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) - names.update(set(unary_unary.inlines)) - - unary_stream = _assemble( - service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, - _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) - names.update(set(unary_stream.inlines)) - - stream_unary = _assemble( - service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, - _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) - names.update(set(stream_unary.inlines)) - - stream_stream = _assemble( - service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, - _EventStreamStreamMethod, _IDENTITY, control, pool) - names.update(set(stream_stream.inlines)) - - methods = list(unary_unary.methods) - methods.extend(unary_stream.methods) - methods.extend(stream_unary.methods) - methods.extend(stream_stream.methods) - adaptations = dict(unary_unary.adaptations) - adaptations.update(unary_stream.adaptations) - adaptations.update(stream_unary.adaptations) - adaptations.update(stream_stream.adaptations) - inlines = dict(unary_unary.inlines) - inlines.update(unary_stream.inlines) - inlines.update(stream_unary.inlines) - inlines.update(stream_stream.inlines) - events = dict(unary_unary.events) - events.update(unary_stream.events) - events.update(stream_unary.events) - events.update(stream_stream.events) - - return TestServiceDigest( - service.name(), - methods, - inlines, - events, - _MultiMethodImplementation(adaptations, control, pool), - unary_unary.messages, - unary_stream.messages, - stream_unary.messages, - stream_stream.messages) diff --git a/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py deleted file mode 100644 index 0f0b0e3d52..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/event_invocation_synchronous_event_service_test_case.py +++ /dev/null @@ -1,362 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -import abc -import unittest - -from grpc.framework.face import interfaces -from grpc.framework.face.testing import callback as testing_callback -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case - -_TIMEOUT = 3 - - -class EventInvocationSynchronousEventServiceTestCase( - test_case.FaceTestCase, coverage.FullCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, None) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.event_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - response = callback.response() - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - responses = callback.responses() - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - response = callback.response() - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - responses = callback.responses() - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - # pylint: disable=cell-var-from-loop - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - first_callback = testing_callback.Callback() - second_callback = testing_callback.Callback() - - def make_second_invocation(first_response): - first_callback.complete(first_response) - self.stub.event_value_in_value_out( - name, second_request, second_callback.complete, - second_callback.abort, _TIMEOUT) - - self.stub.event_value_in_value_out( - name, first_request, make_second_invocation, first_callback.abort, - _TIMEOUT) - second_callback.block_until_terminated() - - first_response = first_callback.response() - second_response = second_callback.response() - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for unused_test_messages in test_messages_sequence: - callback = testing_callback.Callback() - - self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.fail(): - self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.fail(): - self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - with self.control.fail(): - unused_call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - with self.control.fail(): - unused_call, request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - request_consumer.terminate() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) - - def testParallelInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - first_callback = testing_callback.Callback() - second_request = test_messages.request() - second_callback = testing_callback.Callback() - - self.stub.event_value_in_value_out( - name, first_request, first_callback.complete, first_callback.abort, - _TIMEOUT) - self.stub.event_value_in_value_out( - name, second_request, second_callback.complete, - second_callback.abort, _TIMEOUT) - first_callback.block_until_terminated() - second_callback.block_until_terminated() - - first_response = first_callback.response() - second_response = second_callback.response() - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - @unittest.skip('TODO(nathaniel): implement.') - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - def testCancelledUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - with self.control.pause(): - call = self.stub.event_value_in_value_out( - name, request, callback.complete, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = testing_callback.Callback() - - call = self.stub.event_value_in_stream_out( - name, request, callback, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = testing_callback.Callback() - - call, request_consumer = self.stub.event_stream_in_value_out( - name, callback.complete, callback.abort, _TIMEOUT) - for request in requests: - request_consumer.consume(request) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) - - def testCancelledStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for unused_test_messages in test_messages_sequence: - callback = testing_callback.Callback() - - call, unused_request_consumer = self.stub.event_stream_in_stream_out( - name, callback, callback.abort, _TIMEOUT) - call.cancel() - callback.block_until_terminated() - - self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) diff --git a/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py deleted file mode 100644 index 21bf9a4248..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py +++ /dev/null @@ -1,376 +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. - -"""A test to verify an implementation of the Face layer of RPC Framework.""" - -import abc -import contextlib -import threading -import unittest - -from grpc.framework.face import exceptions -from grpc.framework.face.testing import control -from grpc.framework.face.testing import coverage -from grpc.framework.face.testing import digest -from grpc.framework.face.testing import stock_service -from grpc.framework.face.testing import test_case -from grpc.framework.foundation import future -from grpc.framework.foundation import logging_pool - -_TIMEOUT = 3 -_MAXIMUM_POOL_SIZE = 10 - - -class _PauseableIterator(object): - - def __init__(self, upstream): - self._upstream = upstream - self._condition = threading.Condition() - self._paused = False - - @contextlib.contextmanager - def pause(self): - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - def __iter__(self): - return self - - def next(self): - with self._condition: - while self._paused: - self._condition.wait() - return next(self._upstream) - - -class FutureInvocationAsynchronousEventServiceTestCase( - test_case.FaceTestCase, coverage.FullCoverage): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must also extend unittest.TestCase. - """ - __metaclass__ = abc.ABCMeta - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self.control = control.PauseFailControl() - self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) - self.digest = digest.digest( - stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) - - self.stub, self.memo = self.set_up_implementation( - self.digest.name, self.digest.methods, - self.digest.event_method_implementations, None) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self.tear_down_implementation(self.memo) - self.digest_pool.shutdown(wait=True) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - response = response_future.result() - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_future = self.stub.future_stream_in_value_out( - name, request_iterator, _TIMEOUT) - response = response_future.result() - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, request_iterator, _TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self.stub.future_value_in_value_out( - name, first_request, _TIMEOUT) - first_response = first_response_future.result() - - test_messages.verify(first_request, first_response, self) - - second_response_future = self.stub.future_value_in_value_out( - name, second_request, _TIMEOUT) - second_response = second_response_future.result() - - test_messages.verify(second_request, second_response, self) - - def testExpiredUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - multi_callable = self.stub.unary_unary_multi_callable(name) - response_future = multi_callable.future(request, _TIMEOUT) - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testExpiredUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - multi_callable = self.stub.stream_unary_multi_callable(name) - response_future = multi_callable.future(iter(requests), _TIMEOUT) - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testExpiredStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - with self.assertRaises(exceptions.ExpirationError): - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.fail(): - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testFailedUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self.control.fail(), self.assertRaises(exceptions.ExpirationError): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.fail(): - response_future = self.stub.future_stream_in_value_out( - name, iter(requests), _TIMEOUT) - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance( - response_future.exception(), exceptions.ExpirationError) - with self.assertRaises(exceptions.ExpirationError): - response_future.result() - - def testFailedStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self.control.fail(), self.assertRaises(exceptions.ExpirationError): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - list(response_iterator) - - def testParallelInvocations(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self.stub.future_value_in_value_out( - name, first_request, _TIMEOUT) - second_response_future = self.stub.future_value_in_value_out( - name, second_request, _TIMEOUT) - first_response = first_response_future.result() - second_response = second_response_future.result() - - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - @unittest.skip('TODO(nathaniel): implement.') - def testWaitingForSomeButNotAllParallelInvocations(self): - raise NotImplementedError() - - def testCancelledUnaryRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_future = self.stub.future_value_in_value_out( - name, request, _TIMEOUT) - cancel_method_return_value = response_future.cancel() - - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - - def testCancelledUnaryRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.unary_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self.control.pause(): - response_iterator = self.stub.inline_value_in_stream_out( - name, request, _TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(future.CancelledError): - next(response_iterator) - - def testCancelledStreamRequestUnaryResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_unary_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_future = self.stub.future_stream_in_value_out( - name, iter(requests), _TIMEOUT) - cancel_method_return_value = response_future.cancel() - - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - - def testCancelledStreamRequestStreamResponse(self): - for name, test_messages_sequence in ( - self.digest.stream_stream_messages_sequences.iteritems()): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self.control.pause(): - response_iterator = self.stub.inline_stream_in_stream_out( - name, iter(requests), _TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(future.CancelledError): - next(response_iterator) diff --git a/src/python/grpcio/grpc/framework/face/testing/interfaces.py b/src/python/grpcio/grpc/framework/face/testing/interfaces.py deleted file mode 100644 index 5932dabf1e..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/interfaces.py +++ /dev/null @@ -1,117 +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. - -"""Interfaces implemented by data sets used in Face-layer tests.""" - -import abc - -# cardinality is referenced from specification in this module. -from grpc.framework.common import cardinality # pylint: disable=unused-import - - -class Method(object): - """An RPC method to be used in tests of RPC implementations.""" - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def name(self): - """Identify the name of the method. - - Returns: - The name of the method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def cardinality(self): - """Identify the cardinality of the method. - - Returns: - A cardinality.Cardinality value describing the streaming semantics of the - method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def request_class(self): - """Identify the class used for the method's request objects. - - Returns: - The class object of the class to which the method's request objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_class(self): - """Identify the class used for the method's response objects. - - Returns: - The class object of the class to which the method's response objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize the given request object. - - Args: - request: A request object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, serialized_request): - """Synthesize a request object from a given bytestring. - - Args: - serialized_request: A bytestring deserializable into a request object - appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize the given response object. - - Args: - response: A response object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, serialized_response): - """Synthesize a response object from a given bytestring. - - Args: - serialized_response: A bytestring deserializable into a response object - appropriate for this method. - """ - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/serial.py b/src/python/grpcio/grpc/framework/face/testing/serial.py deleted file mode 100644 index 47fc5822de..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/serial.py +++ /dev/null @@ -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. - -"""Utility for serialization in the context of test RPC services.""" - -import collections - - -class Serialization( - collections.namedtuple( - '_Serialization', - ['request_serializers', - 'request_deserializers', - 'response_serializers', - 'response_deserializers'])): - """An aggregation of serialization behaviors for an RPC service. - - Attributes: - request_serializers: A dict from method name to request object serializer - behavior. - request_deserializers: A dict from method name to request object - deserializer behavior. - response_serializers: A dict from method name to response object serializer - behavior. - response_deserializers: A dict from method name to response object - deserializer behavior. - """ - - -def serialization(methods): - """Creates a Serialization from a sequences of interfaces.Method objects.""" - request_serializers = {} - request_deserializers = {} - response_serializers = {} - response_deserializers = {} - for method in methods: - name = method.name() - request_serializers[name] = method.serialize_request - request_deserializers[name] = method.deserialize_request - response_serializers[name] = method.serialize_response - response_deserializers[name] = method.deserialize_response - return Serialization( - request_serializers, request_deserializers, response_serializers, - response_deserializers) diff --git a/src/python/grpcio/grpc/framework/face/testing/service.py b/src/python/grpcio/grpc/framework/face/testing/service.py deleted file mode 100644 index bf54d41d66..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/service.py +++ /dev/null @@ -1,337 +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. - -"""Private interfaces implemented by data sets used in Face-layer tests.""" - -import abc - -# interfaces is referenced from specification in this module. -from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces - - -class UnaryUnaryTestMethodImplementation(interfaces.Method): - """A controllable implementation of a unary-unary RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, request, response_callback, context, control): - """Services an RPC that accepts one message and produces one message. - - Args: - request: The single request message for the RPC. - response_callback: A callback to be called to accept the response message - of the RPC. - context: An face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryUnaryTestMessages(object): - """A type for unary-request-unary-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, response, test_case): - """Verifies that the computed response matches the given request. - - Args: - request: A request message. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class UnaryStreamTestMethodImplementation(interfaces.Method): - """A controllable implementation of a unary-stream RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, request, response_consumer, context, control): - """Services an RPC that takes one message and produces a stream of messages. - - Args: - request: The single request message for the RPC. - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryStreamTestMessages(object): - """A type for unary-request-stream-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, responses, test_case): - """Verifies that the computed responses match the given request. - - Args: - request: A request message. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and responses do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamUnaryTestMethodImplementation(interfaces.Method): - """A controllable implementation of a stream-unary RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, response_callback, context, control): - """Services an RPC that takes a stream of messages and produces one message. - - Args: - response_callback: A callback to be called to accept the response message - of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamUnaryTestMessages(object): - """A type for stream-request-unary-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, response, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamStreamTestMethodImplementation(interfaces.Method): - """A controllable implementation of a stream-stream RPC method.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def service(self, response_consumer, context, control): - """Services an RPC that accepts and produces streams of messages. - - Args: - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face_interfaces.RpcContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamStreamTestMessages(object): - """A type for stream-request-stream-response message pairings.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, responses, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and responses do not match, indicating - that there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class TestService(object): - """A specification of implemented RPC methods to use in tests.""" - - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def name(self): - """Identifies the RPC service name used during the test. - - Returns: - The RPC service name to be used for the test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_unary_scenarios(self): - """Affords unary-request-unary-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair - is a UnaryUnaryTestMethodImplementation object and the second element - is a sequence of UnaryUnaryTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_stream_scenarios(self): - """Affords unary-request-stream-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - UnaryStreamTestMethodImplementation object and the second element is a - sequence of UnaryStreamTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_unary_scenarios(self): - """Affords stream-request-unary-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - StreamUnaryTestMethodImplementation object and the second element is a - sequence of StreamUnaryTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_stream_scenarios(self): - """Affords stream-request-stream-response test methods and their messages. - - Returns: - A dict from method name to pair. The first element of the pair is a - StreamStreamTestMethodImplementation object and the second element is a - sequence of StreamStreamTestMethodMessages objects. - """ - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/face/testing/stock_service.py b/src/python/grpcio/grpc/framework/face/testing/stock_service.py deleted file mode 100644 index 61aaf444a0..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/stock_service.py +++ /dev/null @@ -1,374 +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. - -"""Examples of Python implementations of the stock.proto Stock service.""" - -from grpc.framework.common import cardinality -from grpc.framework.face.testing import service -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util -from grpc._junkdrawer import stock_pb2 - -SYMBOL_FORMAT = 'test symbol:%03d' -STREAM_LENGTH = 400 - -# A test-appropriate security-pricing function. :-P -_price = lambda symbol_name: float(hash(symbol_name) % 4096) - - -def _get_last_trade_price(stock_request, stock_reply_callback, control, active): - """A unary-request, unary-response test method.""" - control.control() - if active(): - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=_price(stock_request.symbol))) - else: - raise abandonment.Abandoned() - - -def _get_last_trade_price_multiple(stock_reply_consumer, control, active): - """A stream-request, stream-response test method.""" - def stock_reply_for_stock_request(stock_request): - control.control() - if active(): - return stock_pb2.StockReply( - symbol=stock_request.symbol, price=_price(stock_request.symbol)) - else: - raise abandonment.Abandoned() - return stream_util.TransformingConsumer( - stock_reply_for_stock_request, stock_reply_consumer) - - -def _watch_future_trades(stock_request, stock_reply_consumer, control, active): - """A unary-request, stream-response test method.""" - base_price = _price(stock_request.symbol) - for index in range(stock_request.num_trades_to_watch): - control.control() - if active(): - stock_reply_consumer.consume( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=base_price + index)) - else: - raise abandonment.Abandoned() - stock_reply_consumer.terminate() - - -def _get_highest_trade_price(stock_reply_callback, control, active): - """A stream-request, unary-response test method.""" - - class StockRequestConsumer(stream.Consumer): - """Keeps an ongoing record of the most valuable symbol yet consumed.""" - - def __init__(self): - self._symbol = None - self._price = None - - def consume(self, stock_request): - control.control() - if active(): - if self._price is None: - self._symbol = stock_request.symbol - self._price = _price(stock_request.symbol) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - self._symbol = stock_request.symbol - self._price = candidate_price - - def terminate(self): - control.control() - if active(): - if self._symbol is None: - raise ValueError() - else: - stock_reply_callback( - stock_pb2.StockReply(symbol=self._symbol, price=self._price)) - self._symbol = None - self._price = None - - def consume_and_terminate(self, stock_request): - control.control() - if active(): - if self._price is None: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, - price=_price(stock_request.symbol))) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=candidate_price)) - else: - stock_reply_callback( - stock_pb2.StockReply( - symbol=self._symbol, price=self._price)) - - self._symbol = None - self._price = None - - return StockRequestConsumer() - - -class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): - """GetLastTradePrice for use in tests.""" - - def name(self): - return 'GetLastTradePrice' - - def cardinality(self): - return cardinality.Cardinality.UNARY_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_callback, context, control): - _get_last_trade_price( - request, response_callback, control, context.is_active) - - -class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): - - def __init__(self): - self._index = 0 - - def request(self): - symbol = SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest(symbol=symbol) - - def verify(self, request, response, test_case): - test_case.assertEqual(request.symbol, response.symbol) - test_case.assertEqual(_price(request.symbol), response.price) - - -class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): - """GetLastTradePriceMultiple for use in tests.""" - - def name(self): - return 'GetLastTradePriceMultiple' - - def cardinality(self): - return cardinality.Cardinality.STREAM_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_consumer, context, control): - return _get_last_trade_price_multiple( - response_consumer, control, context.is_active) - - -class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): - """Pairs of message streams for use with GetLastTradePriceMultiple.""" - - def __init__(self): - self._index = 0 - - def requests(self): - base_index = self._index - self._index += 1 - return [ - stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) - for index in range(STREAM_LENGTH)] - - def verify(self, requests, responses, test_case): - test_case.assertEqual(len(requests), len(responses)) - for stock_request, stock_reply in zip(requests, responses): - test_case.assertEqual(stock_request.symbol, stock_reply.symbol) - test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) - - -class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): - """WatchFutureTrades for use in tests.""" - - def name(self): - return 'WatchFutureTrades' - - def cardinality(self): - return cardinality.Cardinality.UNARY_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_consumer, context, control): - _watch_future_trades(request, response_consumer, control, context.is_active) - - -class WatchFutureTradesMessages(service.UnaryStreamTestMessages): - """Pairs of a single request message and a sequence of response messages.""" - - def __init__(self): - self._index = 0 - - def request(self): - symbol = SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest( - symbol=symbol, num_trades_to_watch=STREAM_LENGTH) - - def verify(self, request, responses, test_case): - test_case.assertEqual(STREAM_LENGTH, len(responses)) - base_price = _price(request.symbol) - for index, response in enumerate(responses): - test_case.assertEqual(base_price + index, response.price) - - -class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): - """GetHighestTradePrice for use in tests.""" - - def name(self): - return 'GetHighestTradePrice' - - def cardinality(self): - return cardinality.Cardinality.STREAM_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_callback, context, control): - return _get_highest_trade_price( - response_callback, control, context.is_active) - - -class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): - - def requests(self): - return [ - stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) - for index in range(STREAM_LENGTH)] - - def verify(self, requests, response, test_case): - price = None - symbol = None - for stock_request in requests: - current_symbol = stock_request.symbol - current_price = _price(current_symbol) - if price is None or price < current_price: - price = current_price - symbol = current_symbol - test_case.assertEqual(price, response.price) - test_case.assertEqual(symbol, response.symbol) - - -class StockTestService(service.TestService): - """A corpus of test data with one method of each RPC cardinality.""" - - def name(self): - return 'Stock' - - def unary_unary_scenarios(self): - return { - 'GetLastTradePrice': ( - GetLastTradePrice(), [GetLastTradePriceMessages()]), - } - - def unary_stream_scenarios(self): - return { - 'WatchFutureTrades': ( - WatchFutureTrades(), [WatchFutureTradesMessages()]), - } - - def stream_unary_scenarios(self): - return { - 'GetHighestTradePrice': ( - GetHighestTradePrice(), [GetHighestTradePriceMessages()]) - } - - def stream_stream_scenarios(self): - return { - 'GetLastTradePriceMultiple': ( - GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), - } - - -STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio/grpc/framework/face/testing/test_case.py b/src/python/grpcio/grpc/framework/face/testing/test_case.py deleted file mode 100644 index e60e3d1d40..0000000000 --- a/src/python/grpcio/grpc/framework/face/testing/test_case.py +++ /dev/null @@ -1,80 +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. - -"""Tools for creating tests of implementations of the Face layer.""" - -import abc - -# face_interfaces and interfaces are referenced in specification in this module. -from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc.framework.face.testing import interfaces # pylint: disable=unused-import - - -class FaceTestCase(object): - """Describes a test of the Face Layer of RPC Framework. - - Concrete subclasses must also inherit from unittest.TestCase and from at least - one class that defines test methods. - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def set_up_implementation( - self, name, methods, method_implementations, - multi_method_implementation): - """Instantiates the Face Layer implementation under test. - - Args: - name: The service name to be used in the test. - methods: A sequence of interfaces.Method objects describing the RPC - methods that will be called during the test. - method_implementations: A dictionary from string RPC method name to - face_interfaces.MethodImplementation object specifying - implementation of an RPC method. - multi_method_implementation: An face_interfaces.MultiMethodImplementation - or None. - - Returns: - A sequence of length two the first element of which is a - face_interfaces.GenericStub (backed by the given method - implementations), and the second element of which is an arbitrary memo - object to be kept and passed to tearDownImplementation at the conclusion - of the test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def tear_down_implementation(self, memo): - """Destroys the Face layer implementation under test. - - Args: - memo: The object from the second position of the return value of - set_up_implementation. - """ - raise NotImplementedError() diff --git a/src/python/grpcio/grpc/framework/foundation/_later_test.py b/src/python/grpcio/grpc/framework/foundation/_later_test.py deleted file mode 100644 index 6c2459e185..0000000000 --- a/src/python/grpcio/grpc/framework/foundation/_later_test.py +++ /dev/null @@ -1,151 +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. - -"""Tests of the later module.""" - -import threading -import time -import unittest - -from grpc.framework.foundation import later - -TICK = 0.1 - - -class LaterTest(unittest.TestCase): - - def test_simple_delay(self): - lock = threading.Lock() - cell = [0] - return_value = object() - - def computation(): - with lock: - cell[0] += 1 - return return_value - computation_future = later.later(TICK * 2, computation) - - self.assertFalse(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - time.sleep(TICK) - self.assertFalse(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - with lock: - self.assertEqual(0, cell[0]) - time.sleep(TICK * 2) - self.assertTrue(computation_future.done()) - self.assertFalse(computation_future.cancelled()) - with lock: - self.assertEqual(1, cell[0]) - self.assertEqual(return_value, computation_future.result()) - - def test_callback(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback = [None] - def computation(): - with lock: - cell[0] += 1 - computation_future = later.later(TICK * 2, computation) - def callback(outcome): - with lock: - callback_called[0] = True - future_passed_to_callback[0] = outcome - computation_future.add_done_callback(callback) - time.sleep(TICK) - with lock: - self.assertFalse(callback_called[0]) - time.sleep(TICK * 2) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].done()) - - callback_called[0] = False - future_passed_to_callback[0] = None - - computation_future.add_done_callback(callback) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].done()) - - def test_cancel(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback = [None] - def computation(): - with lock: - cell[0] += 1 - computation_future = later.later(TICK * 2, computation) - def callback(outcome): - with lock: - callback_called[0] = True - future_passed_to_callback[0] = outcome - computation_future.add_done_callback(callback) - time.sleep(TICK) - with lock: - self.assertFalse(callback_called[0]) - computation_future.cancel() - self.assertTrue(computation_future.cancelled()) - self.assertFalse(computation_future.running()) - self.assertTrue(computation_future.done()) - with lock: - self.assertTrue(callback_called[0]) - self.assertTrue(future_passed_to_callback[0].cancelled()) - - def test_result(self): - lock = threading.Lock() - cell = [0] - callback_called = [False] - future_passed_to_callback_cell = [None] - return_value = object() - - def computation(): - with lock: - cell[0] += 1 - return return_value - computation_future = later.later(TICK * 2, computation) - - def callback(future_passed_to_callback): - with lock: - callback_called[0] = True - future_passed_to_callback_cell[0] = future_passed_to_callback - computation_future.add_done_callback(callback) - returned_value = computation_future.result() - self.assertEqual(return_value, returned_value) - - # The callback may not yet have been called! Sleep a tick. - time.sleep(TICK) - with lock: - self.assertTrue(callback_called[0]) - self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py b/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py deleted file mode 100644 index 452802da6a..0000000000 --- a/src/python/grpcio/grpc/framework/foundation/_logging_pool_test.py +++ /dev/null @@ -1,64 +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. - -"""Tests for grpc.framework.foundation.logging_pool.""" - -import unittest - -from grpc.framework.foundation import logging_pool - -_POOL_SIZE = 16 - - -class LoggingPoolTest(unittest.TestCase): - - def testUpAndDown(self): - pool = logging_pool.pool(_POOL_SIZE) - pool.shutdown(wait=True) - - with logging_pool.pool(_POOL_SIZE) as pool: - self.assertIsNotNone(pool) - - def testTaskExecuted(self): - test_list = [] - - with logging_pool.pool(_POOL_SIZE) as pool: - pool.submit(lambda: test_list.append(object())).result() - - self.assertTrue(test_list) - - def testException(self): - with logging_pool.pool(_POOL_SIZE) as pool: - raised_exception = pool.submit(lambda: 1/0).exception() - - self.assertIsNotNone(raised_exception) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio/grpc/framework/foundation/stream_testing.py b/src/python/grpcio/grpc/framework/foundation/stream_testing.py deleted file mode 100644 index 098a53d5e7..0000000000 --- a/src/python/grpcio/grpc/framework/foundation/stream_testing.py +++ /dev/null @@ -1,73 +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. - -"""Utilities for testing stream-related code.""" - -from grpc.framework.foundation import stream - - -class TestConsumer(stream.Consumer): - """A stream.Consumer instrumented for testing. - - Attributes: - calls: A sequence of value-termination pairs describing the history of calls - made on this object. - """ - - def __init__(self): - self.calls = [] - - def consume(self, value): - """See stream.Consumer.consume for specification.""" - self.calls.append((value, False)) - - def terminate(self): - """See stream.Consumer.terminate for specification.""" - self.calls.append((None, True)) - - def consume_and_terminate(self, value): - """See stream.Consumer.consume_and_terminate for specification.""" - self.calls.append((value, True)) - - def is_legal(self): - """Reports whether or not a legal sequence of calls has been made.""" - terminated = False - for value, terminal in self.calls: - if terminated: - return False - elif terminal: - terminated = True - elif value is None: - return False - else: # pylint: disable=useless-else-on-loop - return True - - def values(self): - """Returns the sequence of values that have been passed to this Consumer.""" - return [value for value, _ in self.calls if value] diff --git a/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py b/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py deleted file mode 100644 index bf1f09d99d..0000000000 --- a/src/python/grpcio/grpc/framework/interfaces/links/test_cases.py +++ /dev/null @@ -1,333 +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. - -"""Tests of the links interface of RPC Framework.""" - -# unittest is referenced from specification in this module. -import abc -import unittest # pylint: disable=unused-import - -from grpc.framework.common import test_constants -from grpc.framework.interfaces.links import links -from grpc.framework.interfaces.links import test_utilities - - -def at_least_n_payloads_received_predicate(n): - def predicate(ticket_sequence): - payload_count = 0 - for ticket in ticket_sequence: - if ticket.payload is not None: - payload_count += 1 - if n <= payload_count: - return True - else: - return False - return predicate - - -def terminated(ticket_sequence): - return ticket_sequence and ticket_sequence[-1].termination is not None - -_TRANSMISSION_GROUP = 'test.Group' -_TRANSMISSION_METHOD = 'TestMethod' - - -class TransmissionTest(object): - """Tests ticket transmission between two connected links. - - This class must be mixed into a unittest.TestCase that implements the abstract - methods it provides. - """ - __metaclass__ = abc.ABCMeta - - # This is a unittest.TestCase mix-in. - # pylint: disable=invalid-name - - @abc.abstractmethod - def create_transmitting_links(self): - """Creates two connected links for use in this test. - - Returns: - Two links.Links, the first of which will be used on the invocation side - of RPCs and the second of which will be used on the service side of - RPCs. - """ - raise NotImplementedError() - - @abc.abstractmethod - def destroy_transmitting_links(self, invocation_side_link, service_side_link): - """Destroys the two connected links created for this test. - - - Args: - invocation_side_link: The link used on the invocation side of RPCs in - this test. - service_side_link: The link used on the service side of RPCs in this - test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_initial_metadata(self): - """Creates a value for use as invocation-side initial metadata. - - Returns: - A metadata value appropriate for use as invocation-side initial metadata - or None if invocation-side initial metadata transmission is not - supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_terminal_metadata(self): - """Creates a value for use as invocation-side terminal metadata. - - Returns: - A metadata value appropriate for use as invocation-side terminal - metadata or None if invocation-side terminal metadata transmission is - not supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_initial_metadata(self): - """Creates a value for use as service-side initial metadata. - - Returns: - A metadata value appropriate for use as service-side initial metadata or - None if service-side initial metadata transmission is not supported by - the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_terminal_metadata(self): - """Creates a value for use as service-side terminal metadata. - - Returns: - A metadata value appropriate for use as service-side terminal metadata or - None if service-side terminal metadata transmission is not supported by - the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_invocation_completion(self): - """Creates values for use as invocation-side code and message. - - Returns: - An invocation-side code value and an invocation-side message value. - Either or both may be None if invocation-side code and/or - invocation-side message transmission is not supported by the links - under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def create_service_completion(self): - """Creates values for use as service-side code and message. - - Returns: - A service-side code value and a service-side message value. Either or - both may be None if service-side code and/or service-side message - transmission is not supported by the links under test. - """ - raise NotImplementedError() - - @abc.abstractmethod - def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): - """Asserts that transmitted_metadata contains original_metadata. - - Args: - original_metadata: A metadata object used in this test. - transmitted_metadata: A metadata object obtained after transmission - through the system under test. - - Raises: - AssertionError: if the transmitted_metadata object does not contain - original_metadata. - """ - raise NotImplementedError() - - def group_and_method(self): - """Returns the group and method used in this test case. - - Returns: - A pair of the group and method used in this test case. - """ - return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD - - def serialize_request(self, request): - """Serializes a request value used in this test case. - - Args: - request: A request value created by this test case. - - Returns: - A bytestring that is the serialization of the given request. - """ - return request - - def deserialize_request(self, serialized_request): - """Deserializes a request value used in this test case. - - Args: - serialized_request: A bytestring that is the serialization of some request - used in this test case. - - Returns: - The request value encoded by the given bytestring. - """ - return serialized_request - - def serialize_response(self, response): - """Serializes a response value used in this test case. - - Args: - response: A response value created by this test case. - - Returns: - A bytestring that is the serialization of the given response. - """ - return response - - def deserialize_response(self, serialized_response): - """Deserializes a response value used in this test case. - - Args: - serialized_response: A bytestring that is the serialization of some - response used in this test case. - - Returns: - The response value encoded by the given bytestring. - """ - return serialized_response - - def _assert_is_valid_metadata_payload_sequence( - self, ticket_sequence, payloads, initial_metadata, terminal_metadata): - initial_metadata_seen = False - seen_payloads = [] - terminal_metadata_seen = False - - for ticket in ticket_sequence: - if ticket.initial_metadata is not None: - self.assertFalse(initial_metadata_seen) - self.assertFalse(seen_payloads) - self.assertFalse(terminal_metadata_seen) - self.assertMetadataTransmitted(initial_metadata, ticket.initial_metadata) - initial_metadata_seen = True - - if ticket.payload is not None: - self.assertFalse(terminal_metadata_seen) - seen_payloads.append(ticket.payload) - - if ticket.terminal_metadata is not None: - self.assertFalse(terminal_metadata_seen) - self.assertMetadataTransmitted(terminal_metadata, ticket.terminal_metadata) - terminal_metadata_seen = True - self.assertSequenceEqual(payloads, seen_payloads) - - def _assert_is_valid_invocation_sequence( - self, ticket_sequence, group, method, payloads, initial_metadata, - terminal_metadata, termination): - self.assertLess(0, len(ticket_sequence)) - self.assertEqual(group, ticket_sequence[0].group) - self.assertEqual(method, ticket_sequence[0].method) - self._assert_is_valid_metadata_payload_sequence( - ticket_sequence, payloads, initial_metadata, terminal_metadata) - self.assertIs(termination, ticket_sequence[-1].termination) - - def _assert_is_valid_service_sequence( - self, ticket_sequence, payloads, initial_metadata, terminal_metadata, - code, message, termination): - self.assertLess(0, len(ticket_sequence)) - self._assert_is_valid_metadata_payload_sequence( - ticket_sequence, payloads, initial_metadata, terminal_metadata) - self.assertEqual(code, ticket_sequence[-1].code) - self.assertEqual(message, ticket_sequence[-1].message) - self.assertIs(termination, ticket_sequence[-1].termination) - - def setUp(self): - self._invocation_link, self._service_link = self.create_transmitting_links() - self._invocation_mate = test_utilities.RecordingLink() - self._service_mate = test_utilities.RecordingLink() - self._invocation_link.join_link(self._invocation_mate) - self._service_link.join_link(self._service_mate) - - def tearDown(self): - self.destroy_transmitting_links(self._invocation_link, self._service_link) - - def testSimplestRoundTrip(self): - """Tests transmission of one ticket in each direction.""" - invocation_operation_id = object() - invocation_payload = b'\x07' * 1023 - timeout = test_constants.LONG_TIMEOUT - invocation_initial_metadata = self.create_invocation_initial_metadata() - invocation_terminal_metadata = self.create_invocation_terminal_metadata() - invocation_code, invocation_message = self.create_invocation_completion() - service_payload = b'\x08' * 1025 - service_initial_metadata = self.create_service_initial_metadata() - service_terminal_metadata = self.create_service_terminal_metadata() - service_code, service_message = self.create_service_completion() - - original_invocation_ticket = links.Ticket( - invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, - links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, - invocation_payload, invocation_terminal_metadata, invocation_code, - invocation_message, links.Ticket.Termination.COMPLETION) - self._invocation_link.accept_ticket(original_invocation_ticket) - - # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the - # invocation-side ticket sequence shouldn't require granting allowance for - # another payload. - self._service_mate.block_until_tickets_satisfy( - at_least_n_payloads_received_predicate(1)) - service_operation_id = self._service_mate.tickets()[0].operation_id - self._service_link.accept_ticket( - links.Ticket( - service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, - None, 1, None, None, None, None, None, None)) - - self._service_mate.block_until_tickets_satisfy(terminated) - self._assert_is_valid_invocation_sequence( - self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, - (invocation_payload,), invocation_initial_metadata, - invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) - - original_service_ticket = links.Ticket( - service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, - timeout, 0, service_initial_metadata, service_payload, - service_terminal_metadata, service_code, service_message, - links.Ticket.Termination.COMPLETION) - self._service_link.accept_ticket(original_service_ticket) - self._invocation_mate.block_until_tickets_satisfy(terminated) - self._assert_is_valid_service_sequence( - self._invocation_mate.tickets(), (service_payload,), - service_initial_metadata, service_terminal_metadata, service_code, - service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py b/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py deleted file mode 100644 index 6c2e3346aa..0000000000 --- a/src/python/grpcio/grpc/framework/interfaces/links/test_utilities.py +++ /dev/null @@ -1,66 +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. - -"""State and behavior appropriate for use in tests.""" - -import threading - -from grpc.framework.interfaces.links import links - - -class RecordingLink(links.Link): - """A Link that records every ticket passed to it.""" - - def __init__(self): - self._condition = threading.Condition() - self._tickets = [] - - def accept_ticket(self, ticket): - with self._condition: - self._tickets.append(ticket) - self._condition.notify_all() - - def join_link(self, link): - pass - - def block_until_tickets_satisfy(self, predicate): - """Blocks until the received tickets satisfy the given predicate. - - Args: - predicate: A callable that takes a sequence of tickets and returns a - boolean value. - """ - with self._condition: - while not predicate(self._tickets): - self._condition.wait() - - def tickets(self): - """Returns a copy of the list of all tickets received by this Link.""" - with self._condition: - return tuple(self._tickets) diff --git a/src/python/grpcio_test/grpc_test/__init__.py b/src/python/grpcio_test/grpc_test/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/_adapter/.gitignore b/src/python/grpcio_test/grpc_test/_adapter/.gitignore new file mode 100644 index 0000000000..a6f96cd6db --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/.gitignore @@ -0,0 +1,5 @@ +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio_test/grpc_test/_adapter/__init__.py b/src/python/grpcio_test/grpc_test/_adapter/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py new file mode 100644 index 0000000000..a1f776211c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _face_test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_c_test.py b/src/python/grpcio_test/grpc_test/_adapter/_c_test.py new file mode 100644 index 0000000000..fe020e2a9c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_c_test.py @@ -0,0 +1,55 @@ +# 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 time +import unittest + +from grpc._adapter import _c +from grpc._adapter import _types + + +class CTypeSmokeTest(unittest.TestCase): + + def testCompletionQueueUpDown(self): + completion_queue = _c.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + completion_queue = _c.CompletionQueue() + serv = _c.Server(completion_queue, []) + del serv + del completion_queue + + def testChannelUpDown(self): + channel = _c.Channel('[::]:0', []) + del channel + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py new file mode 100644 index 0000000000..0d01ebc8dc --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py b/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py new file mode 100644 index 0000000000..dfbd0b60af --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_face_test_case.py @@ -0,0 +1,106 @@ +# 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. + +"""Common construction and destruction for GRPC-backed Face-layer tests.""" + +import unittest + +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import util +from grpc.framework.base import implementations as base_implementations +from grpc.framework.face import implementations as face_implementations +from grpc.framework.foundation import logging_pool +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import serial +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_MAXIMUM_TIMEOUT = 90 +_MAXIMUM_POOL_SIZE = 4 + + +class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): + """Provides abstract Face-layer tests a GRPC-backed implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = face_implementations.servicer( + pool, method_implementations, multi_method_implementation) + + serialization = serial.serialization(methods) + + fore_link = fore.ForeLink( + pool, serialization.request_deserializers, + serialization.response_serializers, None, ()) + fore_link.start() + port = fore_link.port() + rear_link = rear.RearLink( + 'localhost', port, pool, + serialization.request_serializers, + serialization.response_deserializers, False, None, None, None) + rear_link.start() + front = base_implementations.front_link(pool, pool, pool) + back = base_implementations.back_link( + servicer, pool, pool, pool, _TIMEOUT, _MAXIMUM_TIMEOUT) + fore_link.join_rear_link(back) + back.join_fore_link(fore_link) + rear_link.join_fore_link(front) + front.join_rear_link(rear_link) + + stub = face_implementations.generic_stub(front, pool) + return stub, (rear_link, fore_link, front, back) + + def tear_down_implementation(self, memo): + rear_link, fore_link, front, back = memo + # TODO(nathaniel): Waiting for the front and back to idle possibly should + # not be necessary - investigate as part of graceful shutdown work. + util.wait_for_idle(front) + util.wait_for_idle(back) + rear_link.stop() + fore_link.stop() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @unittest.skip('Service-side failure not transmitted by GRPC.') + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py new file mode 100644 index 0000000000..ea4a6a0bae --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test._adapter import _face_test_case +from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _face_test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py new file mode 100644 index 0000000000..27a5b82e9c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py @@ -0,0 +1,434 @@ +# 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 Queue +import threading +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 + + +def _drive_completion_queue(completion_queue, event_queue): + while True: + event = completion_queue.get(_FUTURE) + if event.kind is _low.Event.Kind.STOP: + break + event_queue.put(event) + + +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.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + 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_events.get() + server_terminal_event_two = self.server_events.get() + 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_events.get() + client_terminal_event_two = self.client_events.get() + 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]) + self.assertSetEqual(set(key for key, _ in finish_accepted.metadata), + set((server_trailing_metadata_key, + 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.server_events = Queue.Queue() + self.server_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.server_completion_queue, self.server_events)) + self.server_completion_queue_thread.start() + + self.client_completion_queue = _low.CompletionQueue() + self.channel = _low.Channel('%s:%d' % (self.host, port), None) + self.client_events = Queue.Queue() + self.client_completion_queue_thread = threading.Thread( + target=_drive_completion_queue, + args=(self.client_completion_queue, self.client_events)) + self.client_completion_queue_thread.start() + + def tearDown(self): + self.server.stop() + self.server_completion_queue.stop() + self.client_completion_queue.stop() + self.server_completion_queue_thread.join() + self.client_completion_queue_thread.join() + del self.server + + 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_events.get() + server_call = service_accepted.service_acceptance.call + + server_call.accept(self.server_completion_queue, finish_tag) + server_call.premetadata() + + metadata_accepted = self.client_events.get() + self.assertIsNotNone(metadata_accepted) + + for datum in test_data: + client_call.write(datum, write_tag) + write_accepted = self.client_events.get() + + server_call.read(read_tag) + read_accepted = self.server_events.get() + server_data.append(read_accepted.bytes) + + server_call.write(read_accepted.bytes, write_tag) + write_accepted = self.server_events.get() + self.assertIsNotNone(write_accepted) + + client_call.read(read_tag) + read_accepted = self.client_events.get() + 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_events.get() + server_terminal_event_two = self.server_events.get() + 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_events.get() + 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) + diff --git a/src/python/grpcio_test/grpc_test/_adapter/_links_test.py b/src/python/grpcio_test/grpc_test/_adapter/_links_test.py new file mode 100644 index 0000000000..c4686b327a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_links_test.py @@ -0,0 +1,277 @@ +# 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. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import threading +import unittest + +from grpc._adapter import fore +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool +from grpc_test._adapter import _proto_scenarios +from grpc_test._adapter import _test_links + +_IDENTITY = lambda x: x +_TIMEOUT = 32 + + +# TODO(nathaniel): End-to-end metadata testing. +def _transform_metadata(unused_metadata): + return ( + ('one unused key', 'one unused value'), + ('another unused key', 'another unused value'), +) + + +class RoundTripTest(unittest.TestCase): + + def setUp(self): + self.fore_link_pool = logging_pool.pool(8) + self.rear_link_pool = logging_pool.pool(8) + + def tearDown(self): + self.rear_link_pool.shutdown(wait=True) + self.fore_link_pool.shutdown(wait=True) + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_fore_link = _test_links.ForeLink(None, None) + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE): + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, 0, + interfaces.BackToFrontTicket.Kind.COMPLETION, None) + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: None}, + {test_method: None}, False, None, None, None, + metadata_transformer=_transform_metadata) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is + interfaces.BackToFrontTicket.Kind.CONTINUATION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_fore_link.condition: + self.assertIs( + test_fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testEntireRoundTrip(self): + test_operation_id = object() + test_method = 'test method' + test_front_to_back_datum = b'\x07' + test_back_to_front_datum = b'\x08' + test_fore_link = _test_links.ForeLink(None, None) + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + if front_to_back_ticket.payload is None: + payload = None + else: + payload = test_back_to_front_datum + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if payload is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + payload) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, {test_method: _IDENTITY}, + {test_method: _IDENTITY}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, interfaces.FrontToBackTicket.Kind.ENTIRE, + test_method, interfaces.ServicedSubscription.Kind.FULL, None, + test_front_to_back_datum, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + front_to_back_payloads = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + back_to_front_payloads = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTupleEqual((test_front_to_back_datum,), front_to_back_payloads) + self.assertTupleEqual((test_back_to_front_datum,), back_to_front_payloads) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_method = scenario.method() + test_fore_link = _test_links.ForeLink(None, None) + rear_lock = threading.Lock() + rear_sequence_number = [0] + def rear_action(front_to_back_ticket, fore_link): + with rear_lock: + if front_to_back_ticket.payload is not None: + response = scenario.response_for_request(front_to_back_ticket.payload) + else: + response = None + terminal = front_to_back_ticket.kind in ( + interfaces.FrontToBackTicket.Kind.COMPLETION, + interfaces.FrontToBackTicket.Kind.ENTIRE) + if response is not None or terminal: + if terminal: + kind = interfaces.BackToFrontTicket.Kind.COMPLETION + else: + kind = interfaces.BackToFrontTicket.Kind.CONTINUATION + back_to_front_ticket = interfaces.BackToFrontTicket( + front_to_back_ticket.operation_id, rear_sequence_number[0], kind, + response) + rear_sequence_number[0] += 1 + fore_link.accept_back_to_front_ticket(back_to_front_ticket) + test_rear_link = _test_links.RearLink(rear_action, None) + + fore_link = fore.ForeLink( + self.fore_link_pool, {test_method: scenario.deserialize_request}, + {test_method: scenario.serialize_response}, None, ()) + fore_link.join_rear_link(test_rear_link) + test_rear_link.join_fore_link(fore_link) + fore_link.start() + port = fore_link.port() + + rear_link = rear.RearLink( + 'localhost', port, self.rear_link_pool, + {test_method: scenario.serialize_request}, + {test_method: scenario.deserialize_response}, False, None, None, None) + rear_link.join_fore_link(test_fore_link) + test_fore_link.join_rear_link(rear_link) + rear_link.start() + + commencement_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, + interfaces.FrontToBackTicket.Kind.COMMENCEMENT, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, + _TIMEOUT) + fore_sequence_number = 1 + rear_link.accept_front_to_back_ticket(commencement_ticket) + for request in scenario.requests(): + continuation_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.CONTINUATION, None, None, None, + request, None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(continuation_ticket) + completion_ticket = interfaces.FrontToBackTicket( + test_operation_id, fore_sequence_number, + interfaces.FrontToBackTicket.Kind.COMPLETION, None, None, None, None, + None) + fore_sequence_number += 1 + rear_link.accept_front_to_back_ticket(completion_ticket) + + with test_fore_link.condition: + while (not test_fore_link.tickets or + test_fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.COMPLETION): + test_fore_link.condition.wait() + + rear_link.stop() + fore_link.stop() + + with test_rear_link.condition: + requests = tuple( + ticket.payload for ticket in test_rear_link.tickets + if ticket.payload is not None) + with test_fore_link.condition: + responses = tuple( + ticket.payload for ticket in test_fore_link.tickets + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(requests)) + self.assertTrue(scenario.verify_responses(responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py b/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py new file mode 100644 index 0000000000..9b5758f60f --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_lonely_rear_link_test.py @@ -0,0 +1,100 @@ +# 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import rear +from grpc.framework.base import interfaces +from grpc.framework.foundation import logging_pool +from grpc_test._adapter import _test_links + +_IDENTITY = lambda x: x +_TIMEOUT = 2 + + +class LonelyRearLinkTest(unittest.TestCase): + + def setUp(self): + self.pool = logging_pool.pool(8) + + def tearDown(self): + self.pool.shutdown(wait=True) + + def testUpAndDown(self): + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {}, {}, False, None, None, None) + + rear_link.start() + rear_link.stop() + + def _perform_lonely_client_test_with_ticket_kind( + self, front_to_back_ticket_kind): + test_operation_id = object() + test_method = 'test method' + fore_link = _test_links.ForeLink(None, None) + + rear_link = rear.RearLink( + 'nonexistent', 54321, self.pool, {test_method: None}, + {test_method: None}, False, None, None, None) + rear_link.join_fore_link(fore_link) + rear_link.start() + + front_to_back_ticket = interfaces.FrontToBackTicket( + test_operation_id, 0, front_to_back_ticket_kind, test_method, + interfaces.ServicedSubscription.Kind.FULL, None, None, _TIMEOUT) + rear_link.accept_front_to_back_ticket(front_to_back_ticket) + + with fore_link.condition: + while True: + if (fore_link.tickets and + fore_link.tickets[-1].kind is not + interfaces.BackToFrontTicket.Kind.CONTINUATION): + break + fore_link.condition.wait() + + rear_link.stop() + + with fore_link.condition: + self.assertIsNot( + fore_link.tickets[-1].kind, + interfaces.BackToFrontTicket.Kind.COMPLETION) + + def testLonelyClientCommencementTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.COMMENCEMENT) + + def testLonelyClientEntireTicket(self): + self._perform_lonely_client_test_with_ticket_kind( + interfaces.FrontToBackTicket.Kind.ENTIRE) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py new file mode 100644 index 0000000000..9a8edfad0c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_low_test.py @@ -0,0 +1,199 @@ +# 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 threading +import time +import unittest + +from grpc._adapter import _types +from grpc._adapter import _low + + +def WaitForEvents(completion_queues, deadline): + """ + Args: + completion_queues: list of completion queues to wait for events on + deadline: absolute deadline to wait until + + Returns: + a sequence of events of length len(completion_queues). + """ + + results = [None] * len(completion_queues) + lock = threading.Lock() + threads = [] + def set_ith_result(i, completion_queue): + result = completion_queue.next(deadline) + with lock: + print i, completion_queue, result, time.time() - deadline + results[i] = result + for i, completion_queue in enumerate(completion_queues): + thread = threading.Thread(target=set_ith_result, + args=[i, completion_queue]) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + return results + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + 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 + del self.server + + 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) + + client_no_event, request_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 2) + self.assertEquals(client_no_event, None) + 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)) + got_initial_metadata = dict(request_event.results[0].initial_metadata) + self.assertEquals( + dict(client_initial_metadata), + dict((x, got_initial_metadata[x]) for x in zip(*client_initial_metadata)[0])) + 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, server_event, = WaitForEvents([self.client_completion_queue, self.server_completion_queue], time.time() + 1) + + 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__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py b/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py new file mode 100644 index 0000000000..b3d6ec8607 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py @@ -0,0 +1,261 @@ +# 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc_test._junkdrawer import math_pb2 + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def method(self): + """Access the test method name. + + Returns: + The test method name. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def method(self): + return 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _STREAM_LENGTH = 200 + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(_STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def method(self): + return 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_test_links.py b/src/python/grpcio_test/grpc_test/_adapter/_test_links.py new file mode 100644 index 0000000000..86c7e61b17 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_adapter/_test_links.py @@ -0,0 +1,80 @@ +# 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. + +"""Links suitable for use in tests.""" + +import threading + +from grpc.framework.base import interfaces + + +class ForeLink(interfaces.ForeLink): + """A ForeLink suitable for use in tests of RearLinks.""" + + def __init__(self, action, rear_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.rear_link = rear_link + + def accept_back_to_front_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, rear_link = self.action, self.rear_link + + if action is not None: + action(ticket, rear_link) + + def join_rear_link(self, rear_link): + with self.condition: + self.rear_link = rear_link + + +class RearLink(interfaces.RearLink): + """A RearLink suitable for use in tests of ForeLinks.""" + + def __init__(self, action, fore_link): + self.condition = threading.Condition() + self.tickets = [] + self.action = action + self.fore_link = fore_link + + def accept_front_to_back_ticket(self, ticket): + with self.condition: + self.tickets.append(ticket) + self.condition.notify_all() + action, fore_link = self.action, self.fore_link + + if action is not None: + action(ticket, fore_link) + + def join_fore_link(self, fore_link): + with self.condition: + self.fore_link = fore_link diff --git a/src/python/grpcio_test/grpc_test/_cython/.gitignore b/src/python/grpcio_test/grpc_test/_cython/.gitignore new file mode 100644 index 0000000000..c315029288 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/.gitignore @@ -0,0 +1,7 @@ +*.h +*.c +*.a +*.so +*.dll +*.pyc +*.pyd diff --git a/src/python/grpcio_test/grpc_test/_cython/__init__.py b/src/python/grpcio_test/grpc_test/_cython/__init__.py new file mode 100644 index 0000000000..b89398809f --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/__init__.py @@ -0,0 +1,28 @@ +# 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. diff --git a/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py b/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py new file mode 100644 index 0000000000..9bab930e56 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py @@ -0,0 +1,187 @@ +# 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. + +# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in +# grpc._cython.low should transparently support the semantics expected of +# grpc._adapter._low. + +import time +import unittest + +from grpc._adapter import _types +from grpc._cython import adapter_low as _low + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = _low.CompletionQueue() + self.server = _low.Server(self.server_completion_queue, []) + self.port = self.server.add_http2_port('[::]:0') + self.client_completion_queue = _low.CompletionQueue() + self.client_channel = _low.Channel('localhost:%d'%self.port, []) + + self.server.start() + + def tearDown(self): + self.server.shutdown() + del self.client_channel + + 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 + del self.server + + @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') + 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.assertEqual(_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.assertEqual(_types.CallError.OK, client_start_batch_result) + + request_event = self.server_completion_queue.next(DEADLINE) + self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) + self.assertIsInstance(request_event.call, _low.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(1, len(request_event.results)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.results[0].initial_metadata)) + self.assertEqual(METHOD, request_event.call_details.method) + self.assertEqual(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.assertEqual(_types.CallError.OK, server_start_batch_result) + + client_event = self.client_completion_queue.next(DEADLINE) + server_event = self.server_completion_queue.next(DEADLINE) + + self.assertEqual(6, len(client_event.results)) + found_client_op_types = set() + for client_result in client_event.results: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == _types.OpType.RECV_INITIAL_METADATA: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.initial_metadata)) + elif client_result.type == _types.OpType.RECV_MESSAGE: + self.assertEqual(RESPONSE, client_result.message) + elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.trailing_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) + self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) + self.assertEqual(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.assertEqual(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.assertEqual(REQUEST, server_result.message) + elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: + self.assertFalse(server_result.cancelled) + self.assertEqual(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__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py new file mode 100644 index 0000000000..637506b42e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py @@ -0,0 +1,262 @@ +# 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 time +import unittest + +from grpc._cython import cygrpc +from grpc_test._cython import test_utilities + + +class TypeSmokeTest(unittest.TestCase): + + def testStringsInUtilitiesUpDown(self): + self.assertEqual(0, cygrpc.StatusCode.ok) + metadatum = cygrpc.Metadatum('a', 'b') + self.assertEqual('a'.encode(), metadatum.key) + self.assertEqual('b'.encode(), metadatum.value) + metadata = cygrpc.Metadata([metadatum]) + self.assertEqual(1, len(metadata)) + self.assertEqual(metadatum.key, metadata[0].key) + + def testMetadataIteration(self): + metadata = cygrpc.Metadata([ + cygrpc.Metadatum('a', 'b'), cygrpc.Metadatum('c', 'd')]) + iterator = iter(metadata) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'a'.encode()) + self.assertEqual(metadatum.value, 'b'.encode()) + metadatum = next(iterator) + self.assertIsInstance(metadatum, cygrpc.Metadatum) + self.assertEqual(metadatum.key, 'c'.encode()) + self.assertEqual(metadatum.value, 'd'.encode()) + with self.assertRaises(StopIteration): + next(iterator) + + def testOperationsIteration(self): + operations = cygrpc.Operations([ + cygrpc.operation_send_message('asdf')]) + iterator = iter(operations) + operation = next(iterator) + self.assertIsInstance(operation, cygrpc.Operation) + # `Operation`s are write-only structures; can't directly debug anything out + # of them. Just check that we stop iterating. + with self.assertRaises(StopIteration): + next(iterator) + + def testTimespec(self): + now = time.time() + timespec = cygrpc.Timespec(now) + self.assertAlmostEqual(now, float(timespec), places=8) + + def testCompletionQueueUpDown(self): + completion_queue = cygrpc.CompletionQueue() + del completion_queue + + def testServerUpDown(self): + server = cygrpc.Server(cygrpc.ChannelArgs([])) + del server + + def testChannelUpDown(self): + channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) + del channel + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartNoExplicitShutdown(self): + server = cygrpc.Server() + completion_queue = cygrpc.CompletionQueue() + server.register_completion_queue(completion_queue) + port = server.add_http2_port('[::]:0') + self.assertIsInstance(port, int) + server.start() + del server + + @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testServerStartShutdown(self): + completion_queue = cygrpc.CompletionQueue() + server = cygrpc.Server() + server.add_http2_port('[::]:0') + server.register_completion_queue(completion_queue) + server.start() + shutdown_tag = object() + server.shutdown(completion_queue, shutdown_tag) + event = completion_queue.poll() + self.assertEqual(cygrpc.CompletionType.operation_complete, event.type) + self.assertIs(shutdown_tag, event.tag) + del server + del completion_queue + + +class InsecureServerInsecureClient(unittest.TestCase): + + def setUp(self): + self.server_completion_queue = cygrpc.CompletionQueue() + self.server = cygrpc.Server() + self.server.register_completion_queue(self.server_completion_queue) + self.port = self.server.add_http2_port('[::]:0') + self.server.start() + self.client_completion_queue = cygrpc.CompletionQueue() + self.client_channel = cygrpc.Channel('localhost:{}'.format(self.port)) + + def tearDown(self): + del self.server + 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 = b'key' + CLIENT_METADATA_ASCII_VALUE = b'val' + CLIENT_METADATA_BIN_KEY = b'key-bin' + CLIENT_METADATA_BIN_VALUE = b'\0'*1000 + SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' + SERVER_INITIAL_METADATA_VALUE = b'whodawha?' + SERVER_TRAILING_METADATA_KEY = b'California_is_in_a_drought' + SERVER_TRAILING_METADATA_VALUE = b'zomg it is' + SERVER_STATUS_CODE = cygrpc.StatusCode.ok + SERVER_STATUS_DETAILS = b'our work is never over' + REQUEST = b'in death a member of project mayhem has a name' + RESPONSE = b'his name is robert paulson' + METHOD = b'twinkies' + HOST = b'hostess' + + cygrpc_deadline = cygrpc.Timespec(DEADLINE) + + server_request_tag = object() + request_call_result = self.server.request_call( + self.server_completion_queue, self.server_completion_queue, + server_request_tag) + + self.assertEqual(cygrpc.CallError.ok, request_call_result) + + client_call_tag = object() + client_call = self.client_channel.create_call(self.client_completion_queue, + METHOD, HOST, cygrpc_deadline) + client_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, + CLIENT_METADATA_ASCII_VALUE), + cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) + client_start_batch_result = client_call.start_batch(cygrpc.Operations([ + cygrpc.operation_send_initial_metadata(client_initial_metadata), + cygrpc.operation_send_message(REQUEST), + cygrpc.operation_send_close_from_client(), + cygrpc.operation_receive_initial_metadata(), + cygrpc.operation_receive_message(), + cygrpc.operation_receive_status_on_client() + ]), client_call_tag) + self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) + client_event_future = test_utilities.CompletionQueuePollFuture( + self.client_completion_queue, cygrpc_deadline) + + request_event = self.server_completion_queue.poll(cygrpc_deadline) + self.assertEqual(cygrpc.CompletionType.operation_complete, + request_event.type) + self.assertIsInstance(request_event.operation_call, cygrpc.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(0, len(request_event.batch_operations)) + self.assertEqual(dict(client_initial_metadata), + dict(request_event.request_metadata)) + self.assertEqual(METHOD, request_event.request_call_details.method) + self.assertEqual(HOST, request_event.request_call_details.host) + self.assertLess( + abs(DEADLINE - float(request_event.request_call_details.deadline)), + DEADLINE_TOLERANCE) + + server_call_tag = object() + server_call = request_event.operation_call + server_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, + SERVER_INITIAL_METADATA_VALUE)]) + server_trailing_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, + SERVER_TRAILING_METADATA_VALUE)]) + server_start_batch_result = server_call.start_batch([ + cygrpc.operation_send_initial_metadata(server_initial_metadata), + cygrpc.operation_receive_message(), + cygrpc.operation_send_message(RESPONSE), + cygrpc.operation_receive_close_on_server(), + cygrpc.operation_send_status_from_server( + server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) + ], server_call_tag) + self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) + + client_event = client_event_future.result() + server_event = self.server_completion_queue.poll(cygrpc_deadline) + + self.assertEqual(6, len(client_event.batch_operations)) + found_client_op_types = set() + for client_result in client_event.batch_operations: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == cygrpc.OperationType.receive_initial_metadata: + self.assertEqual(dict(server_initial_metadata), + dict(client_result.received_metadata)) + elif client_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(RESPONSE, client_result.received_message.bytes()) + elif client_result.type == cygrpc.OperationType.receive_status_on_client: + self.assertEqual(dict(server_trailing_metadata), + dict(client_result.received_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, + client_result.received_status_details) + self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.send_message, + cygrpc.OperationType.send_close_from_client, + cygrpc.OperationType.receive_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.receive_status_on_client + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.batch_operations)) + found_server_op_types = set() + for server_result in server_event.batch_operations: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(REQUEST, server_result.received_message.bytes()) + elif server_result.type == cygrpc.OperationType.receive_close_on_server: + self.assertFalse(server_result.received_cancelled) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.send_message, + cygrpc.OperationType.receive_close_on_server, + cygrpc.OperationType.send_status_from_server + ]), found_server_op_types) + + del client_call + del server_call + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/test_utilities.py b/src/python/grpcio_test/grpc_test/_cython/test_utilities.py new file mode 100644 index 0000000000..21ea3075b4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_cython/test_utilities.py @@ -0,0 +1,46 @@ +# 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 threading + +from grpc._cython._cygrpc import completion_queue + + +class CompletionQueuePollFuture: + + def __init__(self, completion_queue, deadline): + def poller_function(): + self._event_result = completion_queue.poll(deadline) + self._event_result = None + self._thread = threading.Thread(target=poller_function) + self._thread.start() + + def result(self): + self._thread.join() + return self._event_result diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py b/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py b/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py new file mode 100644 index 0000000000..20165955b4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py @@ -0,0 +1,266 @@ +# 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the math.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: math.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='math.proto', + package='math', + serialized_pb=_b('\n\nmath.proto\x12\x04math\",\n\x07\x44ivArgs\x12\x10\n\x08\x64ividend\x18\x01 \x02(\x03\x12\x0f\n\x07\x64ivisor\x18\x02 \x02(\x03\"/\n\x08\x44ivReply\x12\x10\n\x08quotient\x18\x01 \x02(\x03\x12\x11\n\tremainder\x18\x02 \x02(\x03\"\x18\n\x07\x46ibArgs\x12\r\n\x05limit\x18\x01 \x01(\x03\"\x12\n\x03Num\x12\x0b\n\x03num\x18\x01 \x02(\x03\"\x19\n\x08\x46ibReply\x12\r\n\x05\x63ount\x18\x01 \x02(\x03\x32\xa4\x01\n\x04Math\x12&\n\x03\x44iv\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00\x12.\n\x07\x44ivMany\x12\r.math.DivArgs\x1a\x0e.math.DivReply\"\x00(\x01\x30\x01\x12#\n\x03\x46ib\x12\r.math.FibArgs\x1a\t.math.Num\"\x00\x30\x01\x12\x1f\n\x03Sum\x12\t.math.Num\x1a\t.math.Num\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_DIVARGS = _descriptor.Descriptor( + name='DivArgs', + full_name='math.DivArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='dividend', full_name='math.DivArgs.dividend', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='divisor', full_name='math.DivArgs.divisor', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=64, +) + + +_DIVREPLY = _descriptor.Descriptor( + name='DivReply', + full_name='math.DivReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='quotient', full_name='math.DivReply.quotient', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='remainder', full_name='math.DivReply.remainder', index=1, + number=2, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=66, + serialized_end=113, +) + + +_FIBARGS = _descriptor.Descriptor( + name='FibArgs', + full_name='math.FibArgs', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='limit', full_name='math.FibArgs.limit', index=0, + number=1, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=115, + serialized_end=139, +) + + +_NUM = _descriptor.Descriptor( + name='Num', + full_name='math.Num', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='num', full_name='math.Num.num', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=141, + serialized_end=159, +) + + +_FIBREPLY = _descriptor.Descriptor( + name='FibReply', + full_name='math.FibReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='count', full_name='math.FibReply.count', index=0, + number=1, type=3, cpp_type=2, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=161, + serialized_end=186, +) + +DESCRIPTOR.message_types_by_name['DivArgs'] = _DIVARGS +DESCRIPTOR.message_types_by_name['DivReply'] = _DIVREPLY +DESCRIPTOR.message_types_by_name['FibArgs'] = _FIBARGS +DESCRIPTOR.message_types_by_name['Num'] = _NUM +DESCRIPTOR.message_types_by_name['FibReply'] = _FIBREPLY + +DivArgs = _reflection.GeneratedProtocolMessageType('DivArgs', (_message.Message,), dict( + DESCRIPTOR = _DIVARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivArgs) + )) +_sym_db.RegisterMessage(DivArgs) + +DivReply = _reflection.GeneratedProtocolMessageType('DivReply', (_message.Message,), dict( + DESCRIPTOR = _DIVREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.DivReply) + )) +_sym_db.RegisterMessage(DivReply) + +FibArgs = _reflection.GeneratedProtocolMessageType('FibArgs', (_message.Message,), dict( + DESCRIPTOR = _FIBARGS, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibArgs) + )) +_sym_db.RegisterMessage(FibArgs) + +Num = _reflection.GeneratedProtocolMessageType('Num', (_message.Message,), dict( + DESCRIPTOR = _NUM, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.Num) + )) +_sym_db.RegisterMessage(Num) + +FibReply = _reflection.GeneratedProtocolMessageType('FibReply', (_message.Message,), dict( + DESCRIPTOR = _FIBREPLY, + __module__ = 'math_pb2' + # @@protoc_insertion_point(class_scope:math.FibReply) + )) +_sym_db.RegisterMessage(FibReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py b/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py new file mode 100644 index 0000000000..eef18f82d6 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py @@ -0,0 +1,152 @@ +# 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. + +# TODO(nathaniel): Remove this from source control after having made +# generation from the stock.proto source part of GRPC's build-and-test +# process. + +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stock.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='stock.proto', + package='stock', + serialized_pb=_b('\n\x0bstock.proto\x12\x05stock\">\n\x0cStockRequest\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x1e\n\x13num_trades_to_watch\x18\x02 \x01(\x05:\x01\x30\"+\n\nStockReply\x12\r\n\x05price\x18\x01 \x01(\x02\x12\x0e\n\x06symbol\x18\x02 \x01(\t2\x96\x02\n\x05Stock\x12=\n\x11GetLastTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x12I\n\x19GetLastTradePriceMultiple\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01\x30\x01\x12?\n\x11WatchFutureTrades\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00\x30\x01\x12\x42\n\x14GetHighestTradePrice\x12\x13.stock.StockRequest\x1a\x11.stock.StockReply\"\x00(\x01') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_STOCKREQUEST = _descriptor.Descriptor( + name='StockRequest', + full_name='stock.StockRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockRequest.symbol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='num_trades_to_watch', full_name='stock.StockRequest.num_trades_to_watch', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=84, +) + + +_STOCKREPLY = _descriptor.Descriptor( + name='StockReply', + full_name='stock.StockReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='price', full_name='stock.StockReply.price', index=0, + number=1, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='symbol', full_name='stock.StockReply.symbol', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=86, + serialized_end=129, +) + +DESCRIPTOR.message_types_by_name['StockRequest'] = _STOCKREQUEST +DESCRIPTOR.message_types_by_name['StockReply'] = _STOCKREPLY + +StockRequest = _reflection.GeneratedProtocolMessageType('StockRequest', (_message.Message,), dict( + DESCRIPTOR = _STOCKREQUEST, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockRequest) + )) +_sym_db.RegisterMessage(StockRequest) + +StockReply = _reflection.GeneratedProtocolMessageType('StockReply', (_message.Message,), dict( + DESCRIPTOR = _STOCKREPLY, + __module__ = 'stock_pb2' + # @@protoc_insertion_point(class_scope:stock.StockReply) + )) +_sym_db.RegisterMessage(StockReply) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/grpcio_test/grpc_test/_links/__init__.py b/src/python/grpcio_test/grpc_test/_links/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py b/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py new file mode 100644 index 0000000000..abe240e07a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py @@ -0,0 +1,88 @@ +# 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. + +"""A test of invocation-side code unconnected to an RPC server.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc.framework.interfaces.links import links +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_cases +from grpc_test.framework.interfaces.links import test_utilities + +_NULL_BEHAVIOR = lambda unused_argument: None + + +class LonelyInvocationLinkTest(unittest.TestCase): + + def testUpAndDown(self): + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link(channel, 'nonexistent', {}, {}) + + invocation_link.start() + invocation_link.stop() + + def _test_lonely_invocation_with_termination(self, termination): + test_operation_id = object() + test_group = 'test package.Test Service' + test_method = 'test method' + invocation_link_mate = test_utilities.RecordingLink() + + channel = _intermediary_low.Channel('nonexistent:54321', None) + invocation_link = invocation.invocation_link( + channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, + {(test_group, test_method): _NULL_BEHAVIOR}) + invocation_link.join_link(invocation_link_mate) + invocation_link.start() + + ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, + None, None, None, None, termination) + invocation_link.accept_ticket(ticket) + invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + + self.assertIsNot( + invocation_link_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def testLonelyInvocationLinkWithCommencementTicket(self): + self._test_lonely_invocation_with_termination(None) + + def testLonelyInvocationLinkWithEntireTicket(self): + self._test_lonely_invocation_with_termination( + links.Ticket.Termination.COMPLETION) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py b/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py new file mode 100644 index 0000000000..0d74d66297 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py @@ -0,0 +1,261 @@ +# 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. + +"""Test scenarios using protocol buffers.""" + +import abc +import threading + +from grpc_test._junkdrawer import math_pb2 +from grpc_test.framework.common import test_constants + + +class ProtoScenario(object): + """An RPC test scenario using protocol buffers.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def group_and_method(self): + """Access the test group and method. + + Returns: + The test group and method as a pair. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize a request protocol buffer. + + Args: + request: A request protocol buffer. + + Returns: + The bytestring serialization of the given request protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, request_bytestring): + """Deserialize a request protocol buffer. + + Args: + request_bytestring: The bytestring serialization of a request protocol + buffer. + + Returns: + The request protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize a response protocol buffer. + + Args: + response: A response protocol buffer. + + Returns: + The bytestring serialization of the given response protocol buffer. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, response_bytestring): + """Deserialize a response protocol buffer. + + Args: + response_bytestring: The bytestring serialization of a response protocol + buffer. + + Returns: + The response protocol buffer deserialized from the given byte string. + """ + raise NotImplementedError() + + @abc.abstractmethod + def requests(self): + """Access the sequence of requests for this scenario. + + Returns: + A sequence of request protocol buffers. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_for_request(self, request): + """Access the response for a particular request. + + Args: + request: A request protocol buffer. + + Returns: + The response protocol buffer appropriate for the given request. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_requests(self, experimental_requests): + """Verify the requests transmitted through the system under test. + + Args: + experimental_requests: The request protocol buffers transmitted through + the system under test. + + Returns: + True if the requests satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify_responses(self, experimental_responses): + """Verify the responses transmitted through the system under test. + + Args: + experimental_responses: The response protocol buffers transmitted through + the system under test. + + Returns: + True if the responses satisfy this test scenario; False otherwise. + """ + raise NotImplementedError() + + +class EmptyScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + raise ValueError('This should not be necessary to call!') + + def deserialize_request(self, request_bytestring): + raise ValueError('This should not be necessary to call!') + + def serialize_response(self, response): + raise ValueError('This should not be necessary to call!') + + def deserialize_response(self, response_bytestring): + raise ValueError('This should not be necessary to call!') + + def requests(self): + return () + + def response_for_request(self, request): + raise ValueError('This should not be necessary to call!') + + def verify_requests(self, experimental_requests): + return not experimental_requests + + def verify_responses(self, experimental_responses): + return not experimental_responses + + +class BidirectionallyUnaryScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _DIVIDEND = 59 + _DIVISOR = 7 + _QUOTIENT = 8 + _REMAINDER = 3 + + _REQUEST = math_pb2.DivArgs(dividend=_DIVIDEND, divisor=_DIVISOR) + _RESPONSE = math_pb2.DivReply(quotient=_QUOTIENT, remainder=_REMAINDER) + + def group_and_method(self): + return 'math.Math', 'Div' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return [self._REQUEST] + + def response_for_request(self, request): + return self._RESPONSE + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == (self._REQUEST,) + + def verify_responses(self, experimental_responses): + return tuple(experimental_responses) == (self._RESPONSE,) + + +class BidirectionallyStreamingScenario(ProtoScenario): + """A scenario that transmits no protocol buffers in either direction.""" + + _REQUESTS = tuple( + math_pb2.DivArgs(dividend=59 + index, divisor=7 + index) + for index in range(test_constants.STREAM_LENGTH)) + + def __init__(self): + self._lock = threading.Lock() + self._responses = [] + + def group_and_method(self): + return 'math.Math', 'DivMany' + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, request_bytestring): + return math_pb2.DivArgs.FromString(request_bytestring) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, response_bytestring): + return math_pb2.DivReply.FromString(response_bytestring) + + def requests(self): + return self._REQUESTS + + def response_for_request(self, request): + quotient, remainder = divmod(request.dividend, request.divisor) + response = math_pb2.DivReply(quotient=quotient, remainder=remainder) + with self._lock: + self._responses.append(response) + return response + + def verify_requests(self, experimental_requests): + return tuple(experimental_requests) == self._REQUESTS + + def verify_responses(self, experimental_responses): + with self._lock: + return tuple(experimental_responses) == tuple(self._responses) diff --git a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py new file mode 100644 index 0000000000..0531fa1d33 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py @@ -0,0 +1,231 @@ +# 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 transmission of tickets across gRPC-on-the-wire.""" + +import unittest + +from grpc._adapter import _intermediary_low +from grpc._links import invocation +from grpc._links import service +from grpc.framework.interfaces.links import links +from grpc_test._links import _proto_scenarios +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_cases +from grpc_test.framework.interfaces.links import test_utilities + +_IDENTITY = lambda x: x + + +class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): + + def create_transmitting_links(self): + service_link = service.service_link( + {self.group_and_method(): self.deserialize_request}, + {self.group_and_method(): self.serialize_response}) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {self.group_and_method(): self.serialize_request}, + {self.group_and_method(): self.deserialize_response}) + invocation_link.start() + return invocation_link, service_link + + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + invocation_side_link.stop() + service_side_link.stop_gracefully() + + def create_invocation_initial_metadata(self): + return ( + ('first invocation initial metadata key', 'just a string value'), + ('second invocation initial metadata key', '0123456789'), + ('third invocation initial metadata key-bin', '\x00\x57' * 100), + ) + + def create_invocation_terminal_metadata(self): + return None + + def create_service_initial_metadata(self): + return ( + ('first service initial metadata key', 'just another string value'), + ('second service initial metadata key', '9876543210'), + ('third service initial metadata key-bin', '\x00\x59\x02' * 100), + ) + + def create_service_terminal_metadata(self): + return ( + ('first service terminal metadata key', 'yet another string value'), + ('second service terminal metadata key', 'abcdefghij'), + ('third service terminal metadata key-bin', '\x00\x37' * 100), + ) + + def create_invocation_completion(self): + return None, None + + def create_service_completion(self): + return _intermediary_low.Code.OK, 'An exuberant test "details" message!' + + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + # we need to filter out any additional metadata added in transmitted_metadata + # since implementations are allowed to add to what is sent (in any position) + keys, _ = zip(*original_metadata) + self.assertSequenceEqual( + original_metadata, + [x for x in transmitted_metadata if x[0] in keys]) + + +class RoundTripTest(unittest.TestCase): + + def testZeroMessageRoundTrip(self): + test_operation_id = object() + test_group = 'test package.Test Group' + test_method = 'test method' + identity_transformation = {(test_group, test_method): _IDENTITY} + test_code = _intermediary_low.Code.OK + test_message = 'a test message' + + service_link = service.service_link( + identity_transformation, identity_transformation) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', identity_transformation, identity_transformation) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + + service_ticket = links.Ticket( + service_mate.tickets()[-1].operation_id, 0, None, None, None, None, + None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + self.assertIs( + service_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + self.assertIs( + invocation_mate.tickets()[-1].termination, + links.Ticket.Termination.COMPLETION) + + def _perform_scenario_test(self, scenario): + test_operation_id = object() + test_group, test_method = scenario.group_and_method() + test_code = _intermediary_low.Code.OK + test_message = 'a scenario test message' + + service_link = service.service_link( + {(test_group, test_method): scenario.deserialize_request}, + {(test_group, test_method): scenario.serialize_response}) + service_mate = test_utilities.RecordingLink() + service_link.join_link(service_mate) + port = service_link.add_port(0, None) + service_link.start() + channel = _intermediary_low.Channel('localhost:%d' % port, None) + invocation_link = invocation.invocation_link( + channel, 'localhost', + {(test_group, test_method): scenario.serialize_request}, + {(test_group, test_method): scenario.deserialize_response}) + invocation_mate = test_utilities.RecordingLink() + invocation_link.join_link(invocation_mate) + invocation_link.start() + + invocation_ticket = links.Ticket( + test_operation_id, 0, test_group, test_method, + links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, + None, None, None, None, None) + invocation_link.accept_ticket(invocation_ticket) + requests = scenario.requests() + for request_index, request in enumerate(requests): + request_ticket = links.Ticket( + test_operation_id, 1 + request_index, None, None, None, None, 1, None, + request, None, None, None, None) + invocation_link.accept_ticket(request_ticket) + service_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + response_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_index, None, None, + None, None, 1, None, scenario.response_for_request(request), None, + None, None, None) + service_link.accept_ticket(response_ticket) + invocation_mate.block_until_tickets_satisfy( + test_cases.at_least_n_payloads_received_predicate(1 + request_index)) + request_count = len(requests) + invocation_completion_ticket = links.Ticket( + test_operation_id, request_count + 1, None, None, None, None, None, + None, None, None, None, None, links.Ticket.Termination.COMPLETION) + invocation_link.accept_ticket(invocation_completion_ticket) + service_mate.block_until_tickets_satisfy(test_cases.terminated) + service_completion_ticket = links.Ticket( + service_mate.tickets()[0].operation_id, request_count, None, None, None, + None, None, None, None, None, test_code, test_message, + links.Ticket.Termination.COMPLETION) + service_link.accept_ticket(service_completion_ticket) + invocation_mate.block_until_tickets_satisfy(test_cases.terminated) + + invocation_link.stop() + service_link.stop_gracefully() + + observed_requests = tuple( + ticket.payload for ticket in service_mate.tickets() + if ticket.payload is not None) + observed_responses = tuple( + ticket.payload for ticket in invocation_mate.tickets() + if ticket.payload is not None) + self.assertTrue(scenario.verify_requests(observed_requests)) + self.assertTrue(scenario.verify_responses(observed_responses)) + + def testEmptyScenario(self): + self._perform_scenario_test(_proto_scenarios.EmptyScenario()) + + def testBidirectionallyUnaryScenario(self): + self._perform_scenario_test(_proto_scenarios.BidirectionallyUnaryScenario()) + + def testBidirectionallyStreamingScenario(self): + self._perform_scenario_test( + _proto_scenarios.BidirectionallyStreamingScenario()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/early_adopter/__init__.py b/src/python/grpcio_test/grpc_test/early_adopter/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/early_adopter/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py b/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py new file mode 100644 index 0000000000..611637e8b8 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/early_adopter/implementations_test.py @@ -0,0 +1,180 @@ +# 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. + +# TODO(nathaniel): Expand this test coverage. + +"""Test of the GRPC-backed ForeLink and RearLink.""" + +import unittest + +from grpc.early_adopter import implementations +from grpc.framework.alpha import utilities +from grpc_test._junkdrawer import math_pb2 + +SERVICE_NAME = 'math.Math' + +DIV = 'Div' +DIV_MANY = 'DivMany' +FIB = 'Fib' +SUM = 'Sum' + +def _fibbonacci(limit): + left, right = 0, 1 + for _ in xrange(limit): + yield left + left, right = right, left + right + + +def _div(request, unused_context): + return math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _div_many(request_iterator, unused_context): + for request in request_iterator: + yield math_pb2.DivReply( + quotient=request.dividend / request.divisor, + remainder=request.dividend % request.divisor) + + +def _fib(request, unused_context): + for number in _fibbonacci(request.limit): + yield math_pb2.Num(num=number) + + +def _sum(request_iterator, unused_context): + accumulation = 0 + for request in request_iterator: + accumulation += request.num + return math_pb2.Num(num=accumulation) + + +_INVOCATION_DESCRIPTIONS = { + DIV: utilities.unary_unary_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + DIV_MANY: utilities.stream_stream_invocation_description( + math_pb2.DivArgs.SerializeToString, math_pb2.DivReply.FromString), + FIB: utilities.unary_stream_invocation_description( + math_pb2.FibArgs.SerializeToString, math_pb2.Num.FromString), + SUM: utilities.stream_unary_invocation_description( + math_pb2.Num.SerializeToString, math_pb2.Num.FromString), +} + +_SERVICE_DESCRIPTIONS = { + DIV: utilities.unary_unary_service_description( + _div, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + DIV_MANY: utilities.stream_stream_service_description( + _div_many, math_pb2.DivArgs.FromString, + math_pb2.DivReply.SerializeToString), + FIB: utilities.unary_stream_service_description( + _fib, math_pb2.FibArgs.FromString, math_pb2.Num.SerializeToString), + SUM: utilities.stream_unary_service_description( + _sum, math_pb2.Num.FromString, math_pb2.Num.SerializeToString), +} + +_TIMEOUT = 3 + + +class EarlyAdopterImplementationsTest(unittest.TestCase): + + def setUp(self): + self.server = implementations.server( + SERVICE_NAME, _SERVICE_DESCRIPTIONS, 0) + self.server.start() + port = self.server.port() + self.stub = implementations.stub( + SERVICE_NAME, _INVOCATION_DESCRIPTIONS, 'localhost', port) + + def tearDown(self): + self.server.stop() + + def testUpAndDown(self): + with self.stub: + pass + + def testUnaryUnary(self): + divisor = 59 + dividend = 973 + expected_quotient = dividend / divisor + expected_remainder = dividend % divisor + + with self.stub: + response = self.stub.Div( + math_pb2.DivArgs(divisor=divisor, dividend=dividend), _TIMEOUT) + self.assertEqual(expected_quotient, response.quotient) + self.assertEqual(expected_remainder, response.remainder) + + def testUnaryStream(self): + stream_length = 43 + + with self.stub: + response_iterator = self.stub.Fib( + math_pb2.FibArgs(limit=stream_length), _TIMEOUT) + numbers = tuple(response.num for response in response_iterator) + for early, middle, later in zip(numbers, numbers[:1], numbers[:2]): + self.assertEqual(early + middle, later) + self.assertEqual(stream_length, len(numbers)) + + def testStreamUnary(self): + stream_length = 127 + + with self.stub: + response_future = self.stub.Sum.async( + (math_pb2.Num(num=index) for index in range(stream_length)), + _TIMEOUT) + self.assertEqual( + (stream_length * (stream_length - 1)) / 2, + response_future.result().num) + + def testStreamStream(self): + stream_length = 179 + divisor_offset = 71 + dividend_offset = 1763 + + with self.stub: + response_iterator = self.stub.DivMany( + (math_pb2.DivArgs( + divisor=divisor_offset + index, + dividend=dividend_offset + index) + for index in range(stream_length)), + _TIMEOUT) + for index, response in enumerate(response_iterator): + self.assertEqual( + (dividend_offset + index) / (divisor_offset + index), + response.quotient) + self.assertEqual( + (dividend_offset + index) % (divisor_offset + index), + response.remainder) + self.assertEqual(stream_length, index + 1) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/__init__.py b/src/python/grpcio_test/grpc_test/framework/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/base/__init__.py b/src/python/grpcio_test/grpc_test/framework/base/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py b/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py new file mode 100644 index 0000000000..5a7d1398fd --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/implementations_test.py @@ -0,0 +1,80 @@ +# 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 grpc.framework.base.implementations.""" + +import unittest + +from grpc.framework.base import implementations +from grpc.framework.base import util +from grpc.framework.foundation import logging_pool +from grpc_test.framework.base import interfaces_test_case + +POOL_MAX_WORKERS = 10 +DEFAULT_TIMEOUT = 30 +MAXIMUM_TIMEOUT = 60 + + +class ImplementationsTest( + interfaces_test_case.FrontAndBackTest, unittest.TestCase): + + def setUp(self): + self.memory_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.front_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_work_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_transmission_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.back_utility_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_pool = logging_pool.pool(POOL_MAX_WORKERS) + self.test_servicer = interfaces_test_case.TestServicer(self.test_pool) + self.front = implementations.front_link( + self.front_work_pool, self.front_transmission_pool, + self.front_utility_pool) + self.back = implementations.back_link( + self.test_servicer, self.back_work_pool, self.back_transmission_pool, + self.back_utility_pool, DEFAULT_TIMEOUT, MAXIMUM_TIMEOUT) + self.front.join_rear_link(self.back) + self.back.join_fore_link(self.front) + + def tearDown(self): + util.wait_for_idle(self.back) + util.wait_for_idle(self.front) + self.memory_transmission_pool.shutdown(wait=True) + self.front_work_pool.shutdown(wait=True) + self.front_transmission_pool.shutdown(wait=True) + self.front_utility_pool.shutdown(wait=True) + self.back_work_pool.shutdown(wait=True) + self.back_transmission_pool.shutdown(wait=True) + self.back_utility_pool.shutdown(wait=True) + self.test_pool.shutdown(wait=True) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py b/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py new file mode 100644 index 0000000000..be775ad4e0 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/base/interfaces_test_case.py @@ -0,0 +1,307 @@ +# 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. + +"""Abstract tests against the interfaces of the base layer of RPC Framework.""" + +import threading +import time + +from grpc.framework.base import interfaces +from grpc.framework.base import util +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.foundation import stream_testing + +TICK = 0.1 +SMALL_TIMEOUT = TICK * 50 +STREAM_LENGTH = 100 + +SYNCHRONOUS_ECHO = 'synchronous echo' +ASYNCHRONOUS_ECHO = 'asynchronous echo' +IMMEDIATE_FAILURE = 'immediate failure' +TRIGGERED_FAILURE = 'triggered failure' +WAIT_ON_CONDITION = 'wait on condition' + +EMPTY_OUTCOME_DICT = { + interfaces.Outcome.COMPLETED: 0, + interfaces.Outcome.CANCELLED: 0, + interfaces.Outcome.EXPIRED: 0, + interfaces.Outcome.RECEPTION_FAILURE: 0, + interfaces.Outcome.TRANSMISSION_FAILURE: 0, + interfaces.Outcome.SERVICER_FAILURE: 0, + interfaces.Outcome.SERVICED_FAILURE: 0, + } + + +def _synchronous_echo(output_consumer): + return stream_util.TransformingConsumer(lambda x: x, output_consumer) + + +class AsynchronousEcho(stream.Consumer): + """A stream.Consumer that echoes its input to another stream.Consumer.""" + + def __init__(self, output_consumer, pool): + self._lock = threading.Lock() + self._output_consumer = output_consumer + self._pool = pool + + self._queue = [] + self._spinning = False + + def _spin(self, value, complete): + while True: + if value: + if complete: + self._output_consumer.consume_and_terminate(value) + else: + self._output_consumer.consume(value) + elif complete: + self._output_consumer.terminate() + with self._lock: + if self._queue: + value, complete = self._queue.pop(0) + else: + self._spinning = False + return + + def consume(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, False)) + else: + self._spinning = True + self._pool.submit(self._spin, value, False) + + def terminate(self): + with self._lock: + if self._spinning: + self._queue.append((None, True)) + else: + self._spinning = True + self._pool.submit(self._spin, None, True) + + def consume_and_terminate(self, value): + with self._lock: + if self._spinning: + self._queue.append((value, True)) + else: + self._spinning = True + self._pool.submit(self._spin, value, True) + + +class TestServicer(interfaces.Servicer): + """An interfaces.Servicer with instrumented for testing.""" + + def __init__(self, pool): + self._pool = pool + self.condition = threading.Condition() + self._released = False + + def service(self, name, context, output_consumer): + if name == SYNCHRONOUS_ECHO: + return _synchronous_echo(output_consumer) + elif name == ASYNCHRONOUS_ECHO: + return AsynchronousEcho(output_consumer, self._pool) + elif name == IMMEDIATE_FAILURE: + raise ValueError() + elif name == TRIGGERED_FAILURE: + raise NotImplementedError + elif name == WAIT_ON_CONDITION: + with self.condition: + while not self._released: + self.condition.wait() + return _synchronous_echo(output_consumer) + else: + raise NotImplementedError() + + def release(self): + with self.condition: + self._released = True + self.condition.notify_all() + + +class EasyServicedIngestor(interfaces.ServicedIngestor): + """A trivial implementation of interfaces.ServicedIngestor.""" + + def __init__(self, consumer): + self._consumer = consumer + + def consumer(self, operation_context): + """See interfaces.ServicedIngestor.consumer for specification.""" + return self._consumer + + +class FrontAndBackTest(object): + """A test suite usable against any joined Front and Back.""" + + # Pylint doesn't know that this is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + def testSimplestCall(self): + """Tests the absolute simplest call - a one-ticket fire-and-forget.""" + self.front.operate( + SYNCHRONOUS_ECHO, None, True, SMALL_TIMEOUT, + util.none_serviced_subscription(), 'test trace ID') + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) The ticket is still either in the front waiting to be transmitted + # or is somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the ticket bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) The ticket arrived at the back and the back completed the operation. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.COMPLETED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + # It's true that if the ticket had arrived at the back and the back had + # begun processing that wait_for_idle could hold test execution until the + # back completed the operation, but that doesn't really collapse the + # possibility space down to one solution. + + def testEntireEcho(self): + """Tests a very simple one-ticket-each-way round-trip.""" + test_payload = 'test payload' + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + self.front.operate( + ASYNCHRONOUS_ECHO, test_payload, True, SMALL_TIMEOUT, subscription, + 'test trace ID') + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual([(test_payload, True)], test_consumer.calls) + + def testBidirectionalStreamingEcho(self): + """Tests sending multiple tickets each way.""" + test_payload_template = 'test_payload: %03d' + test_payloads = [test_payload_template % i for i in range(STREAM_LENGTH)] + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + SYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + + for test_payload in test_payloads: + operation.consumer.consume(test_payload) + operation.consumer.terminate() + + util.wait_for_idle(self.front) + util.wait_for_idle(self.back) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertEqual( + 1, self.back.operation_stats()[interfaces.Outcome.COMPLETED]) + self.assertListEqual(test_payloads, test_consumer.values()) + + def testCancellation(self): + """Tests cancelling a long-lived operation.""" + test_consumer = stream_testing.TestConsumer() + subscription = util.full_serviced_subscription( + EasyServicedIngestor(test_consumer)) + + operation = self.front.operate( + ASYNCHRONOUS_ECHO, None, False, SMALL_TIMEOUT, subscription, + 'test trace ID') + operation.cancel() + + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.CANCELLED]) + util.wait_for_idle(self.back) + self.assertListEqual([], test_consumer.calls) + + # Assuming nothing really pathological (such as pauses on the order of + # SMALL_TIMEOUT interfering with this test) there are a two different ways + # the back could have experienced execution up to this point: + # (1) Both tickets are still either in the front waiting to be transmitted + # or are somewhere on the link between the front and the back. The back has + # no idea that this test is even happening. Calling wait_for_idle on it + # would do no good because in this case the back is idle and the call would + # return with the tickets bound for it still in the front or on the link. + back_operation_stats = self.back.operation_stats() + first_back_possibility = EMPTY_OUTCOME_DICT + # (2) Both tickets arrived within SMALL_TIMEOUT of one another at the back. + # The back started processing based on the first ticket and then stopped + # upon receiving the cancellation ticket. + second_back_possibility = dict(EMPTY_OUTCOME_DICT) + second_back_possibility[interfaces.Outcome.CANCELLED] = 1 + self.assertIn( + back_operation_stats, (first_back_possibility, second_back_possibility)) + + def testExpiration(self): + """Tests that operations time out.""" + timeout = TICK * 2 + allowance = TICK # How much extra time to + condition = threading.Condition() + test_payload = 'test payload' + subscription = util.termination_only_serviced_subscription() + start_time = time.time() + + outcome_cell = [None] + termination_time_cell = [None] + def termination_action(outcome): + with condition: + outcome_cell[0] = outcome + termination_time_cell[0] = time.time() + condition.notify() + + with condition: + operation = self.front.operate( + SYNCHRONOUS_ECHO, test_payload, False, timeout, subscription, + 'test trace ID') + operation.context.add_termination_callback(termination_action) + while outcome_cell[0] is None: + condition.wait() + + duration = termination_time_cell[0] - start_time + self.assertLessEqual(timeout, duration) + self.assertLess(duration, timeout + allowance) + self.assertEqual(interfaces.Outcome.EXPIRED, outcome_cell[0]) + util.wait_for_idle(self.front) + self.assertEqual( + 1, self.front.operation_stats()[interfaces.Outcome.EXPIRED]) + util.wait_for_idle(self.back) + self.assertLessEqual( + 1, self.back.operation_stats()[interfaces.Outcome.EXPIRED]) diff --git a/src/python/grpcio_test/grpc_test/framework/common/__init__.py b/src/python/grpcio_test/grpc_test/framework/common/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_constants.py b/src/python/grpcio_test/grpc_test/framework/common/test_constants.py new file mode 100644 index 0000000000..3126d0d82c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_constants.py @@ -0,0 +1,43 @@ +# 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. + +"""Constants shared among tests throughout RPC Framework.""" + +# Value for maximum duration in seconds of RPCs that may time out as part of a +# test. +SHORT_TIMEOUT = 4 +# Absurdly large value for maximum duration in seconds for should-not-time-out +# RPCs made during tests. +LONG_TIMEOUT = 3000 + +# The number of payloads to transmit in streaming tests. +STREAM_LENGTH = 200 + +# The size of thread pools to use in tests. +POOL_SIZE = 10 diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_control.py b/src/python/grpcio_test/grpc_test/framework/common/test_control.py new file mode 100644 index 0000000000..3960c4e649 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_control.py @@ -0,0 +1,87 @@ +# 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py b/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py new file mode 100644 index 0000000000..a7ed3582c4 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py @@ -0,0 +1,116 @@ +# 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. + +"""Governs coverage for tests of RPCs throughout RPC Framework.""" + +import abc + +# This code is designed for use with the unittest module. +# pylint: disable=invalid-name + + +class Coverage(object): + """Specification of test coverage.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/__init__.py b/src/python/grpcio_test/grpc_test/framework/face/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/face/_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/_test_case.py new file mode 100644 index 0000000000..486b6e630e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/_test_case.py @@ -0,0 +1,61 @@ +# 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. + +"""Common lifecycle code for in-memory-ticket-exchange Face-layer tests.""" + +from grpc.framework.face import implementations +from grpc.framework.foundation import logging_pool +from grpc_test.framework.face.testing import base_util +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 10 + + +class FaceTestCase(test_case.FaceTestCase): + """Provides abstract Face-layer tests an in-memory implementation.""" + + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + servicer_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + stub_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + + servicer = implementations.servicer( + servicer_pool, method_implementations, multi_method_implementation) + + linked_pair = base_util.linked_pair(servicer, _TIMEOUT) + stub = implementations.generic_stub(linked_pair.front, stub_pool) + return stub, (servicer_pool, stub_pool, linked_pair) + + def tear_down_implementation(self, memo): + servicer_pool, stub_pool, linked_pair = memo + linked_pair.shut_down() + stub_pool.shutdown(wait=True) + servicer_pool.shutdown(wait=True) diff --git a/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py new file mode 100644 index 0000000000..8674666418 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/blocking_invocation_inline_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import blocking_invocation_inline_service_test_case as test_case + + +class BlockingInvocationInlineServiceTest( + _test_case.FaceTestCase, + test_case.BlockingInvocationInlineServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py new file mode 100644 index 0000000000..dca373ef7c --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/event_invocation_synchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import event_invocation_synchronous_event_service_test_case as test_case + + +class EventInvocationSynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.EventInvocationSynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py b/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py new file mode 100644 index 0000000000..99fdf18123 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/future_invocation_asynchronous_event_service_test.py @@ -0,0 +1,46 @@ +# 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. + +"""One of the tests of the Face layer of RPC Framework.""" + +import unittest + +from grpc_test.framework.face import _test_case +from grpc_test.framework.face.testing import future_invocation_asynchronous_event_service_test_case as test_case + + +class FutureInvocationAsynchronousEventServiceTest( + _test_case.FaceTestCase, + test_case.FutureInvocationAsynchronousEventServiceTestCase, + unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py b/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py b/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py new file mode 100644 index 0000000000..1df1529b27 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py @@ -0,0 +1,102 @@ +# 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. + +"""Utilities for creating Base-layer objects for use in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.base import util as _base_util +from grpc.framework.base import implementations +from grpc.framework.base import in_memory +from grpc.framework.base import interfaces # pylint: disable=unused-import +from grpc.framework.foundation import logging_pool + +_POOL_SIZE_LIMIT = 5 + +_MAXIMUM_TIMEOUT = 90 + + +class LinkedPair(object): + """A Front and Back that are linked to one another. + + Attributes: + front: An interfaces.Front. + back: An interfaces.Back. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def shut_down(self): + """Shuts down this object and releases its resources.""" + raise NotImplementedError() + + +class _LinkedPair(LinkedPair): + + def __init__(self, front, back, pools): + self.front = front + self.back = back + self._pools = pools + + def shut_down(self): + _base_util.wait_for_idle(self.front) + _base_util.wait_for_idle(self.back) + + for pool in self._pools: + pool.shutdown(wait=True) + + +def linked_pair(servicer, default_timeout): + """Creates a Server and Stub linked together for use.""" + link_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + front_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_work_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_transmission_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + back_utility_pool = logging_pool.pool(_POOL_SIZE_LIMIT) + pools = ( + link_pool, + front_work_pool, front_transmission_pool, front_utility_pool, + back_work_pool, back_transmission_pool, back_utility_pool) + + link = in_memory.Link(link_pool) + front = implementations.front_link( + front_work_pool, front_transmission_pool, front_utility_pool) + back = implementations.back_link( + servicer, back_work_pool, back_transmission_pool, back_utility_pool, + default_timeout, _MAXIMUM_TIMEOUT) + front.join_rear_link(link) + link.join_fore_link(front) + back.join_fore_link(link) + link.join_rear_link(back) + + return _LinkedPair(front, back, pools) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py new file mode 100644 index 0000000000..7e1158f96b --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py @@ -0,0 +1,222 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.face import exceptions +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_LONG_TIMEOUT = 45 + + +class BlockingInvocationInlineServiceTestCase( + test_case.FaceTestCase, coverage.BlockingCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.inline_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response = self.stub.blocking_value_in_value_out( + name, request, _LONG_TIMEOUT) + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response = self.stub.blocking_stream_in_value_out( + name, iter(requests), _LONG_TIMEOUT) + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _LONG_TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response = self.stub.blocking_value_in_value_out( + name, first_request, _TIMEOUT) + + test_messages.verify(first_request, first_response, self) + + second_response = self.stub.blocking_value_in_value_out( + name, second_request, _TIMEOUT) + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.unary_unary_multi_callable(name) + multi_callable(request, _TIMEOUT) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + multi_callable = self.stub.stream_unary_multi_callable(name) + multi_callable(iter(requests), _TIMEOUT) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(), self.assertRaises( + exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_value_in_value_out(name, request, _TIMEOUT) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + self.stub.blocking_stream_in_value_out(name, iter(requests), _TIMEOUT) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(), self.assertRaises(exceptions.ServicerError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py b/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py new file mode 100644 index 0000000000..d0e63c8c56 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py @@ -0,0 +1,94 @@ +# 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. + +"""A utility useful in tests of asynchronous, event-driven interfaces.""" + +import threading + +from grpc.framework.foundation import stream + + +class Callback(stream.Consumer): + """A utility object useful in tests of asynchronous code.""" + + def __init__(self): + self._condition = threading.Condition() + self._unary_response = None + self._streamed_responses = [] + self._completed = False + self._abortion = None + + def abort(self, abortion): + with self._condition: + self._abortion = abortion + self._condition.notify_all() + + def complete(self, unary_response): + with self._condition: + self._unary_response = unary_response + self._completed = True + self._condition.notify_all() + + def consume(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + + def terminate(self): + with self._condition: + self._completed = True + self._condition.notify_all() + + def consume_and_terminate(self, streamed_response): + with self._condition: + self._streamed_responses.append(streamed_response) + self._completed = True + self._condition.notify_all() + + def block_until_terminated(self): + with self._condition: + while self._abortion is None and not self._completed: + self._condition.wait() + + def response(self): + with self._condition: + if self._abortion is None: + return self._unary_response + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def responses(self): + with self._condition: + if self._abortion is None: + return list(self._streamed_responses) + else: + raise AssertionError('Aborted with abortion "%s"!' % self._abortion) + + def abortion(self): + with self._condition: + return self._abortion diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/control.py b/src/python/grpcio_test/grpc_test/framework/face/testing/control.py new file mode 100644 index 0000000000..3960c4e649 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/control.py @@ -0,0 +1,87 @@ +# 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. + +"""Code for instructing systems under test to block or fail.""" + +import abc +import contextlib +import threading + + +class Control(object): + """An object that accepts program control from a system under test. + + Systems under test passed a Control should call its control() method + frequently during execution. The control() method may block, raise an + exception, or do nothing, all according to the enclosing test's desire for + the system under test to simulate hanging, failing, or functioning. + """ + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def control(self): + """Potentially does anything.""" + raise NotImplementedError() + + +class PauseFailControl(Control): + """A Control that can be used to pause or fail code under control.""" + + def __init__(self): + self._condition = threading.Condition() + self._paused = False + self._fail = False + + def control(self): + with self._condition: + if self._fail: + raise ValueError() + + while self._paused: + self._condition.wait() + + @contextlib.contextmanager + def pause(self): + """Pauses code under control while controlling code is in context.""" + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + @contextlib.contextmanager + def fail(self): + """Fails code under control while controlling code is in context.""" + with self._condition: + self._fail = True + yield + with self._condition: + self._fail = False diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py b/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py new file mode 100644 index 0000000000..f3aca113fe --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py @@ -0,0 +1,123 @@ +# 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. + +"""Governs coverage for the tests of the Face layer of RPC Framework.""" + +import abc + +# These classes are only valid when inherited by unittest.TestCases. +# pylint: disable=invalid-name + + +class BlockingCoverage(object): + """Specification of test coverage for blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testSuccessfulUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSuccessfulStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testSequentialInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testExpiredStreamRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testFailedStreamRequestStreamResponse(self): + raise NotImplementedError() + + +class FullCoverage(BlockingCoverage): + """Specification of test coverage for non-blocking behaviors.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def testParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledUnaryRequestStreamResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestUnaryResponse(self): + raise NotImplementedError() + + @abc.abstractmethod + def testCancelledStreamRequestStreamResponse(self): + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py b/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py new file mode 100644 index 0000000000..54ff21779a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py @@ -0,0 +1,450 @@ +# 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. + +"""Code for making a service.TestService more amenable to use in tests.""" + +import collections +import threading + +# testing_control, interfaces, and testing_service are referenced from +# specification in this module. +from grpc.framework.common import cardinality +from grpc.framework.common import style +from grpc.framework.face import exceptions +from grpc.framework.face import interfaces as face_interfaces +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.face.testing import control as testing_control # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import service as testing_service # pylint: disable=unused-import + +_IDENTITY = lambda x: x + + +class TestServiceDigest( + collections.namedtuple( + 'TestServiceDigest', + ['name', + 'methods', + 'inline_method_implementations', + 'event_method_implementations', + 'multi_method_implementation', + 'unary_unary_messages_sequences', + 'unary_stream_messages_sequences', + 'stream_unary_messages_sequences', + 'stream_stream_messages_sequences'])): + """A transformation of a service.TestService. + + Attributes: + name: The RPC service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + inline_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + in-line calls to behaviors under test. + event_method_implementations: A dict from RPC method name to + face_interfaces.MethodImplementation object to be used in tests of + event-driven calls to behaviors under test. + multi_method_implementation: A face_interfaces.MultiMethodImplementation to + be used in tests of generic calls to behaviors under test. + unary_unary_messages_sequences: A dict from method name to sequence of + service.UnaryUnaryTestMessages objects to be used to test the method + with the given name. + unary_stream_messages_sequences: A dict from method name to sequence of + service.UnaryStreamTestMessages objects to be used to test the method + with the given name. + stream_unary_messages_sequences: A dict from method name to sequence of + service.StreamUnaryTestMessages objects to be used to test the method + with the given name. + stream_stream_messages_sequences: A dict from method name to sequence of + service.StreamStreamTestMessages objects to be used to test the + method with the given name. + serialization: A serial.Serialization object describing serialization + behaviors for all the RPC methods. + """ + + +class _BufferingConsumer(stream.Consumer): + """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" + + def __init__(self): + self.consumed = [] + self.terminated = False + + def consume(self, value): + self.consumed.append(value) + + def terminate(self): + self.terminated = True + + def consume_and_terminate(self, value): + self.consumed.append(value) + self.terminated = True + + +class _InlineUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control): + self._test_method = unary_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.INLINE + + def unary_unary_inline(self, request, context): + response_list = [] + self._test_method.service( + request, response_list.append, context, self._control) + return response_list.pop(0) + + +class _EventUnaryUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_unary_test_method, control, pool): + self._test_method = unary_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_UNARY + self.style = style.Service.EVENT + + def unary_unary_event(self, request, response_callback, context): + if self._pool is None: + self._test_method.service( + request, response_callback, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_callback, context, + self._control) + + +class _InlineUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control): + self._test_method = unary_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.INLINE + + def unary_stream_inline(self, request, context): + response_consumer = _BufferingConsumer() + self._test_method.service( + request, response_consumer, context, self._control) + for response in response_consumer.consumed: + yield response + + +class _EventUnaryStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, unary_stream_test_method, control, pool): + self._test_method = unary_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.UNARY_STREAM + self.style = style.Service.EVENT + + def unary_stream_event(self, request, response_consumer, context): + if self._pool is None: + self._test_method.service( + request, response_consumer, context, self._control) + else: + self._pool.submit( + self._test_method.service, request, response_consumer, context, + self._control) + + +class _InlineStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control): + self._test_method = stream_unary_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.INLINE + + def stream_unary_inline(self, request_iterator, context): + response_list = [] + request_consumer = self._test_method.service( + response_list.append, context, self._control) + for request in request_iterator: + request_consumer.consume(request) + request_consumer.terminate() + return response_list.pop(0) + + +class _EventStreamUnaryMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_unary_test_method, control, pool): + self._test_method = stream_unary_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_UNARY + self.style = style.Service.EVENT + + def stream_unary_event(self, response_callback, context): + request_consumer = self._test_method.service( + response_callback, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _InlineStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control): + self._test_method = stream_stream_test_method + self._control = control + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.INLINE + + def stream_stream_inline(self, request_iterator, context): + response_consumer = _BufferingConsumer() + request_consumer = self._test_method.service( + response_consumer, context, self._control) + + for request in request_iterator: + request_consumer.consume(request) + while response_consumer.consumed: + yield response_consumer.consumed.pop(0) + response_consumer.terminate() + + +class _EventStreamStreamMethod(face_interfaces.MethodImplementation): + + def __init__(self, stream_stream_test_method, control, pool): + self._test_method = stream_stream_test_method + self._control = control + self._pool = pool + + self.cardinality = cardinality.Cardinality.STREAM_STREAM + self.style = style.Service.EVENT + + def stream_stream_event(self, response_consumer, context): + request_consumer = self._test_method.service( + response_consumer, context, self._control) + if self._pool is None: + return request_consumer + else: + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _UnaryConsumer(stream.Consumer): + """A Consumer that only allows consumption of exactly one value.""" + + def __init__(self, action): + self._lock = threading.Lock() + self._action = action + self._consumed = False + self._terminated = False + + def consume(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + + self._action(value) + + def terminate(self): + with self._lock: + if not self._consumed: + raise ValueError('Unary consumer hasn\'t yet consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._terminated = True + + def consume_and_terminate(self, value): + with self._lock: + if self._consumed: + raise ValueError('Unary consumer already consumed!') + elif self._terminated: + raise ValueError('Unary consumer already terminated!') + else: + self._consumed = True + self._terminated = True + + self._action(value) + + +class _UnaryUnaryAdaptation(object): + + def __init__(self, unary_unary_test_method): + self._method = unary_unary_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service( + request, response_consumer.consume_and_terminate, context, control) + return _UnaryConsumer(action) + + +class _UnaryStreamAdaptation(object): + + def __init__(self, unary_stream_test_method): + self._method = unary_stream_test_method + + def service(self, response_consumer, context, control): + def action(request): + self._method.service(request, response_consumer, context, control) + return _UnaryConsumer(action) + + +class _StreamUnaryAdaptation(object): + + def __init__(self, stream_unary_test_method): + self._method = stream_unary_test_method + + def service(self, response_consumer, context, control): + return self._method.service( + response_consumer.consume_and_terminate, context, control) + + +class _MultiMethodImplementation(face_interfaces.MultiMethodImplementation): + + def __init__(self, methods, control, pool): + self._methods = methods + self._control = control + self._pool = pool + + def service(self, name, response_consumer, context): + method = self._methods.get(name, None) + if method is None: + raise exceptions.NoSuchMethodError(name) + elif self._pool is None: + return method(response_consumer, context, self._control) + else: + request_consumer = method(response_consumer, context, self._control) + return stream_util.ThreadSwitchingConsumer(request_consumer, self._pool) + + +class _Assembly( + collections.namedtuple( + '_Assembly', + ['methods', 'inlines', 'events', 'adaptations', 'messages'])): + """An intermediate structure created when creating a TestServiceDigest.""" + + +def _assemble( + scenarios, names, inline_method_constructor, event_method_constructor, + adapter, control, pool): + """Creates an _Assembly from the given scenarios.""" + methods = [] + inlines = {} + events = {} + adaptations = {} + messages = {} + for name, scenario in scenarios.iteritems(): + if name in names: + raise ValueError('Repeated name "%s"!' % name) + + test_method = scenario[0] + inline_method = inline_method_constructor(test_method, control) + event_method = event_method_constructor(test_method, control, pool) + adaptation = adapter(test_method) + + methods.append(test_method) + inlines[name] = inline_method + events[name] = event_method + adaptations[name] = adaptation + messages[name] = scenario[1] + + return _Assembly(methods, inlines, events, adaptations, messages) + + +def digest(service, control, pool): + """Creates a TestServiceDigest from a TestService. + + Args: + service: A testing_service.TestService. + control: A testing_control.Control. + pool: If RPC methods should be serviced in a separate thread, a thread pool. + None if RPC methods should be serviced in the thread belonging to the + run-time that calls for their service. + + Returns: + A TestServiceDigest synthesized from the given service.TestService. + """ + names = set() + + unary_unary = _assemble( + service.unary_unary_scenarios(), names, _InlineUnaryUnaryMethod, + _EventUnaryUnaryMethod, _UnaryUnaryAdaptation, control, pool) + names.update(set(unary_unary.inlines)) + + unary_stream = _assemble( + service.unary_stream_scenarios(), names, _InlineUnaryStreamMethod, + _EventUnaryStreamMethod, _UnaryStreamAdaptation, control, pool) + names.update(set(unary_stream.inlines)) + + stream_unary = _assemble( + service.stream_unary_scenarios(), names, _InlineStreamUnaryMethod, + _EventStreamUnaryMethod, _StreamUnaryAdaptation, control, pool) + names.update(set(stream_unary.inlines)) + + stream_stream = _assemble( + service.stream_stream_scenarios(), names, _InlineStreamStreamMethod, + _EventStreamStreamMethod, _IDENTITY, control, pool) + names.update(set(stream_stream.inlines)) + + methods = list(unary_unary.methods) + methods.extend(unary_stream.methods) + methods.extend(stream_unary.methods) + methods.extend(stream_stream.methods) + adaptations = dict(unary_unary.adaptations) + adaptations.update(unary_stream.adaptations) + adaptations.update(stream_unary.adaptations) + adaptations.update(stream_stream.adaptations) + inlines = dict(unary_unary.inlines) + inlines.update(unary_stream.inlines) + inlines.update(stream_unary.inlines) + inlines.update(stream_stream.inlines) + events = dict(unary_unary.events) + events.update(unary_stream.events) + events.update(stream_unary.events) + events.update(stream_stream.events) + + return TestServiceDigest( + service.name(), + methods, + inlines, + events, + _MultiMethodImplementation(adaptations, control, pool), + unary_unary.messages, + unary_stream.messages, + stream_unary.messages, + stream_stream.messages) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py new file mode 100644 index 0000000000..18eed53d6e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py @@ -0,0 +1,362 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import unittest + +from grpc.framework.face import interfaces +from grpc_test.framework.face.testing import callback as testing_callback +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 + + +class EventInvocationSynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, None) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + response = callback.response() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + responses = callback.responses() + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + # pylint: disable=cell-var-from-loop + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + first_callback = testing_callback.Callback() + second_callback = testing_callback.Callback() + + def make_second_invocation(first_response): + first_callback.complete(first_response) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + + self.stub.event_value_in_value_out( + name, first_request, make_second_invocation, first_callback.abort, + _TIMEOUT) + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.EXPIRED, callback.abortion()) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.fail(): + self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + with self.control.fail(): + unused_call, request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + request_consumer.terminate() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.SERVICER_FAILURE, callback.abortion()) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + first_callback = testing_callback.Callback() + second_request = test_messages.request() + second_callback = testing_callback.Callback() + + self.stub.event_value_in_value_out( + name, first_request, first_callback.complete, first_callback.abort, + _TIMEOUT) + self.stub.event_value_in_value_out( + name, second_request, second_callback.complete, + second_callback.abort, _TIMEOUT) + first_callback.block_until_terminated() + second_callback.block_until_terminated() + + first_response = first_callback.response() + second_response = second_callback.response() + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + with self.control.pause(): + call = self.stub.event_value_in_value_out( + name, request, callback.complete, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + callback = testing_callback.Callback() + + call = self.stub.event_value_in_stream_out( + name, request, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + callback = testing_callback.Callback() + + call, request_consumer = self.stub.event_stream_in_value_out( + name, callback.complete, callback.abort, _TIMEOUT) + for request in requests: + request_consumer.consume(request) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for unused_test_messages in test_messages_sequence: + callback = testing_callback.Callback() + + call, unused_request_consumer = self.stub.event_stream_in_stream_out( + name, callback, callback.abort, _TIMEOUT) + call.cancel() + callback.block_until_terminated() + + self.assertEqual(interfaces.Abortion.CANCELLED, callback.abortion()) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py new file mode 100644 index 0000000000..3b42914342 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -0,0 +1,376 @@ +# 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. + +"""A test to verify an implementation of the Face layer of RPC Framework.""" + +import abc +import contextlib +import threading +import unittest + +from grpc.framework.face import exceptions +from grpc.framework.foundation import future +from grpc.framework.foundation import logging_pool +from grpc_test.framework.face.testing import control +from grpc_test.framework.face.testing import coverage +from grpc_test.framework.face.testing import digest +from grpc_test.framework.face.testing import stock_service +from grpc_test.framework.face.testing import test_case + +_TIMEOUT = 3 +_MAXIMUM_POOL_SIZE = 10 + + +class _PauseableIterator(object): + + def __init__(self, upstream): + self._upstream = upstream + self._condition = threading.Condition() + self._paused = False + + @contextlib.contextmanager + def pause(self): + with self._condition: + self._paused = True + yield + with self._condition: + self._paused = False + self._condition.notify_all() + + def __iter__(self): + return self + + def next(self): + with self._condition: + while self._paused: + self._condition.wait() + return next(self._upstream) + + +class FutureInvocationAsynchronousEventServiceTestCase( + test_case.FaceTestCase, coverage.FullCoverage): + """A test of the Face layer of RPC Framework. + + Concrete subclasses must also extend unittest.TestCase. + """ + __metaclass__ = abc.ABCMeta + + def setUp(self): + """See unittest.TestCase.setUp for full specification. + + Overriding implementations must call this implementation. + """ + self.control = control.PauseFailControl() + self.digest_pool = logging_pool.pool(_MAXIMUM_POOL_SIZE) + self.digest = digest.digest( + stock_service.STOCK_TEST_SERVICE, self.control, self.digest_pool) + + self.stub, self.memo = self.set_up_implementation( + self.digest.name, self.digest.methods, + self.digest.event_method_implementations, None) + + def tearDown(self): + """See unittest.TestCase.tearDown for full specification. + + Overriding implementations must call this implementation. + """ + self.tear_down_implementation(self.memo) + self.digest_pool.shutdown(wait=True) + + def testSuccessfulUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + response = response_future.result() + + test_messages.verify(request, response, self) + + def testSuccessfulUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(request, responses, self) + + def testSuccessfulStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_future = self.stub.future_stream_in_value_out( + name, request_iterator, _TIMEOUT) + response = response_future.result() + + test_messages.verify(requests, response, self) + + def testSuccessfulStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + request_iterator = _PauseableIterator(iter(requests)) + + # Use of a paused iterator of requests allows us to test that control is + # returned to calling code before the iterator yields any requests. + with request_iterator.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, request_iterator, _TIMEOUT) + responses = list(response_iterator) + + test_messages.verify(requests, responses, self) + + def testSequentialInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + first_response = first_response_future.result() + + test_messages.verify(first_request, first_response, self) + + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + second_response = second_response_future.result() + + test_messages.verify(second_request, second_response, self) + + def testExpiredUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + multi_callable = self.stub.unary_unary_multi_callable(name) + response_future = multi_callable.future(request, _TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testExpiredStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + multi_callable = self.stub.stream_unary_multi_callable(name) + response_future = multi_callable.future(iter(requests), _TIMEOUT) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testExpiredStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) + + def testFailedUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.fail(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + list(response_iterator) + + def testFailedStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.fail(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() + + def testFailedStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is indistinguishable + # from simply not having called its response_consumer before the + # expiration of the RPC. + with self.control.fail(), self.assertRaises(exceptions.ExpirationError): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + list(response_iterator) + + def testParallelInvocations(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + first_request = test_messages.request() + second_request = test_messages.request() + + first_response_future = self.stub.future_value_in_value_out( + name, first_request, _TIMEOUT) + second_response_future = self.stub.future_value_in_value_out( + name, second_request, _TIMEOUT) + first_response = first_response_future.result() + second_response = second_response_future.result() + + test_messages.verify(first_request, first_response, self) + test_messages.verify(second_request, second_response, self) + + @unittest.skip('TODO(nathaniel): implement.') + def testWaitingForSomeButNotAllParallelInvocations(self): + raise NotImplementedError() + + def testCancelledUnaryRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_future = self.stub.future_value_in_value_out( + name, request, _TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledUnaryRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.unary_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + request = test_messages.request() + + with self.control.pause(): + response_iterator = self.stub.inline_value_in_stream_out( + name, request, _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) + + def testCancelledStreamRequestUnaryResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_unary_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_future = self.stub.future_stream_in_value_out( + name, iter(requests), _TIMEOUT) + cancel_method_return_value = response_future.cancel() + + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) + + def testCancelledStreamRequestStreamResponse(self): + for name, test_messages_sequence in ( + self.digest.stream_stream_messages_sequences.iteritems()): + for test_messages in test_messages_sequence: + requests = test_messages.requests() + + with self.control.pause(): + response_iterator = self.stub.inline_stream_in_stream_out( + name, iter(requests), _TIMEOUT) + response_iterator.cancel() + + with self.assertRaises(future.CancelledError): + next(response_iterator) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py b/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py new file mode 100644 index 0000000000..5932dabf1e --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py @@ -0,0 +1,117 @@ +# 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. + +"""Interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# cardinality is referenced from specification in this module. +from grpc.framework.common import cardinality # pylint: disable=unused-import + + +class Method(object): + """An RPC method to be used in tests of RPC implementations.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identify the name of the method. + + Returns: + The name of the method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def cardinality(self): + """Identify the cardinality of the method. + + Returns: + A cardinality.Cardinality value describing the streaming semantics of the + method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def request_class(self): + """Identify the class used for the method's request objects. + + Returns: + The class object of the class to which the method's request objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def response_class(self): + """Identify the class used for the method's response objects. + + Returns: + The class object of the class to which the method's response objects + belong. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serialize the given request object. + + Args: + request: A request object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Synthesize a request object from a given bytestring. + + Args: + serialized_request: A bytestring deserializable into a request object + appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serialize the given response object. + + Args: + response: A response object appropriate for this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Synthesize a response object from a given bytestring. + + Args: + serialized_response: A bytestring deserializable into a response object + appropriate for this method. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py b/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py new file mode 100644 index 0000000000..47fc5822de --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/serial.py @@ -0,0 +1,70 @@ +# 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. + +"""Utility for serialization in the context of test RPC services.""" + +import collections + + +class Serialization( + collections.namedtuple( + '_Serialization', + ['request_serializers', + 'request_deserializers', + 'response_serializers', + 'response_deserializers'])): + """An aggregation of serialization behaviors for an RPC service. + + Attributes: + request_serializers: A dict from method name to request object serializer + behavior. + request_deserializers: A dict from method name to request object + deserializer behavior. + response_serializers: A dict from method name to response object serializer + behavior. + response_deserializers: A dict from method name to response object + deserializer behavior. + """ + + +def serialization(methods): + """Creates a Serialization from a sequences of interfaces.Method objects.""" + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + for method in methods: + name = method.name() + request_serializers[name] = method.serialize_request + request_deserializers[name] = method.deserialize_request + response_serializers[name] = method.serialize_response + response_deserializers[name] = method.deserialize_response + return Serialization( + request_serializers, request_deserializers, response_serializers, + response_deserializers) diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/service.py b/src/python/grpcio_test/grpc_test/framework/face/testing/service.py new file mode 100644 index 0000000000..ee9d6a3da3 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/service.py @@ -0,0 +1,337 @@ +# 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. + +"""Private interfaces implemented by data sets used in Face-layer tests.""" + +import abc + +# interfaces is referenced from specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces + + +class UnaryUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_callback, context, control): + """Services an RPC that accepts one message and produces one message. + + Args: + request: The single request message for the RPC. + response_callback: A callback to be called to accept the response message + of the RPC. + context: An face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryUnaryTestMessages(object): + """A type for unary-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, response, test_case): + """Verifies that the computed response matches the given request. + + Args: + request: A request message. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class UnaryStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a unary-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, request, response_consumer, context, control): + """Services an RPC that takes one message and produces a stream of messages. + + Args: + request: The single request message for the RPC. + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class UnaryStreamTestMessages(object): + """A type for unary-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def request(self): + """Affords a request message. + + Implementations of this method should return a different message with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A request message. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, request, responses, test_case): + """Verifies that the computed responses match the given request. + + Args: + request: A request message. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the request and responses do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamUnaryTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-unary RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_callback, context, control): + """Services an RPC that takes a stream of messages and produces one message. + + Args: + response_callback: A callback to be called to accept the response message + of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamUnaryTestMessages(object): + """A type for stream-request-unary-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, response, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + response: A response message. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and response do not match, indicating that + there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class StreamStreamTestMethodImplementation(interfaces.Method): + """A controllable implementation of a stream-stream RPC method.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def service(self, response_consumer, context, control): + """Services an RPC that accepts and produces streams of messages. + + Args: + response_consumer: A stream.Consumer to be called to accept the response + messages of the RPC. + context: A face_interfaces.RpcContext object. + control: A test_control.Control to control execution of this method. + + Returns: + A stream.Consumer with which to accept the request messages of the RPC. + The consumer returned from this method may or may not be invoked to + completion: in the case of RPC abortion, RPC Framework will simply stop + passing messages to this object. Implementations must not assume that + this object will be called to completion of the request stream or even + called at all. + + Raises: + abandonment.Abandoned: May or may not be raised when the RPC has been + aborted. + """ + raise NotImplementedError() + + +class StreamStreamTestMessages(object): + """A type for stream-request-stream-response message pairings.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def requests(self): + """Affords a sequence of request messages. + + Implementations of this method should return a different sequences with each + call so that multiple test executions of the test method may be made with + different inputs. + + Returns: + A sequence of request messages. + """ + raise NotImplementedError() + + @abc.abstractmethod + def verify(self, requests, responses, test_case): + """Verifies that the computed response matches the given requests. + + Args: + requests: A sequence of request messages. + responses: A sequence of response messages. + test_case: A unittest.TestCase object affording useful assertion methods. + + Raises: + AssertionError: If the requests and responses do not match, indicating + that there was some problem executing the RPC under test. + """ + raise NotImplementedError() + + +class TestService(object): + """A specification of implemented RPC methods to use in tests.""" + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def name(self): + """Identifies the RPC service name used during the test. + + Returns: + The RPC service name to be used for the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_unary_scenarios(self): + """Affords unary-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair + is a UnaryUnaryTestMethodImplementation object and the second element + is a sequence of UnaryUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def unary_stream_scenarios(self): + """Affords unary-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + UnaryStreamTestMethodImplementation object and the second element is a + sequence of UnaryStreamTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_unary_scenarios(self): + """Affords stream-request-unary-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamUnaryTestMethodImplementation object and the second element is a + sequence of StreamUnaryTestMethodMessages objects. + """ + raise NotImplementedError() + + @abc.abstractmethod + def stream_stream_scenarios(self): + """Affords stream-request-stream-response test methods and their messages. + + Returns: + A dict from method name to pair. The first element of the pair is a + StreamStreamTestMethodImplementation object and the second element is a + sequence of StreamStreamTestMethodMessages objects. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py b/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py new file mode 100644 index 0000000000..0f83ca4db1 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py @@ -0,0 +1,374 @@ +# 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. + +"""Examples of Python implementations of the stock.proto Stock service.""" + +from grpc.framework.common import cardinality +from grpc.framework.foundation import abandonment +from grpc.framework.foundation import stream +from grpc.framework.foundation import stream_util +from grpc_test.framework.face.testing import service +from grpc_test._junkdrawer import stock_pb2 + +SYMBOL_FORMAT = 'test symbol:%03d' +STREAM_LENGTH = 400 + +# A test-appropriate security-pricing function. :-P +_price = lambda symbol_name: float(hash(symbol_name) % 4096) + + +def _get_last_trade_price(stock_request, stock_reply_callback, control, active): + """A unary-request, unary-response test method.""" + control.control() + if active(): + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol))) + else: + raise abandonment.Abandoned() + + +def _get_last_trade_price_multiple(stock_reply_consumer, control, active): + """A stream-request, stream-response test method.""" + def stock_reply_for_stock_request(stock_request): + control.control() + if active(): + return stock_pb2.StockReply( + symbol=stock_request.symbol, price=_price(stock_request.symbol)) + else: + raise abandonment.Abandoned() + return stream_util.TransformingConsumer( + stock_reply_for_stock_request, stock_reply_consumer) + + +def _watch_future_trades(stock_request, stock_reply_consumer, control, active): + """A unary-request, stream-response test method.""" + base_price = _price(stock_request.symbol) + for index in range(stock_request.num_trades_to_watch): + control.control() + if active(): + stock_reply_consumer.consume( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=base_price + index)) + else: + raise abandonment.Abandoned() + stock_reply_consumer.terminate() + + +def _get_highest_trade_price(stock_reply_callback, control, active): + """A stream-request, unary-response test method.""" + + class StockRequestConsumer(stream.Consumer): + """Keeps an ongoing record of the most valuable symbol yet consumed.""" + + def __init__(self): + self._symbol = None + self._price = None + + def consume(self, stock_request): + control.control() + if active(): + if self._price is None: + self._symbol = stock_request.symbol + self._price = _price(stock_request.symbol) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + self._symbol = stock_request.symbol + self._price = candidate_price + + def terminate(self): + control.control() + if active(): + if self._symbol is None: + raise ValueError() + else: + stock_reply_callback( + stock_pb2.StockReply(symbol=self._symbol, price=self._price)) + self._symbol = None + self._price = None + + def consume_and_terminate(self, stock_request): + control.control() + if active(): + if self._price is None: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, + price=_price(stock_request.symbol))) + else: + candidate_price = _price(stock_request.symbol) + if self._price < candidate_price: + stock_reply_callback( + stock_pb2.StockReply( + symbol=stock_request.symbol, price=candidate_price)) + else: + stock_reply_callback( + stock_pb2.StockReply( + symbol=self._symbol, price=self._price)) + + self._symbol = None + self._price = None + + return StockRequestConsumer() + + +class GetLastTradePrice(service.UnaryUnaryTestMethodImplementation): + """GetLastTradePrice for use in tests.""" + + def name(self): + return 'GetLastTradePrice' + + def cardinality(self): + return cardinality.Cardinality.UNARY_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_callback, context, control): + _get_last_trade_price( + request, response_callback, control, context.is_active) + + +class GetLastTradePriceMessages(service.UnaryUnaryTestMessages): + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest(symbol=symbol) + + def verify(self, request, response, test_case): + test_case.assertEqual(request.symbol, response.symbol) + test_case.assertEqual(_price(request.symbol), response.price) + + +class GetLastTradePriceMultiple(service.StreamStreamTestMethodImplementation): + """GetLastTradePriceMultiple for use in tests.""" + + def name(self): + return 'GetLastTradePriceMultiple' + + def cardinality(self): + return cardinality.Cardinality.STREAM_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_consumer, context, control): + return _get_last_trade_price_multiple( + response_consumer, control, context.is_active) + + +class GetLastTradePriceMultipleMessages(service.StreamStreamTestMessages): + """Pairs of message streams for use with GetLastTradePriceMultiple.""" + + def __init__(self): + self._index = 0 + + def requests(self): + base_index = self._index + self._index += 1 + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % (base_index + index)) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, responses, test_case): + test_case.assertEqual(len(requests), len(responses)) + for stock_request, stock_reply in zip(requests, responses): + test_case.assertEqual(stock_request.symbol, stock_reply.symbol) + test_case.assertEqual(_price(stock_request.symbol), stock_reply.price) + + +class WatchFutureTrades(service.UnaryStreamTestMethodImplementation): + """WatchFutureTrades for use in tests.""" + + def name(self): + return 'WatchFutureTrades' + + def cardinality(self): + return cardinality.Cardinality.UNARY_STREAM + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, request, response_consumer, context, control): + _watch_future_trades(request, response_consumer, control, context.is_active) + + +class WatchFutureTradesMessages(service.UnaryStreamTestMessages): + """Pairs of a single request message and a sequence of response messages.""" + + def __init__(self): + self._index = 0 + + def request(self): + symbol = SYMBOL_FORMAT % self._index + self._index += 1 + return stock_pb2.StockRequest( + symbol=symbol, num_trades_to_watch=STREAM_LENGTH) + + def verify(self, request, responses, test_case): + test_case.assertEqual(STREAM_LENGTH, len(responses)) + base_price = _price(request.symbol) + for index, response in enumerate(responses): + test_case.assertEqual(base_price + index, response.price) + + +class GetHighestTradePrice(service.StreamUnaryTestMethodImplementation): + """GetHighestTradePrice for use in tests.""" + + def name(self): + return 'GetHighestTradePrice' + + def cardinality(self): + return cardinality.Cardinality.STREAM_UNARY + + def request_class(self): + return stock_pb2.StockRequest + + def response_class(self): + return stock_pb2.StockReply + + def serialize_request(self, request): + return request.SerializeToString() + + def deserialize_request(self, serialized_request): + return stock_pb2.StockRequest.FromString(serialized_request) + + def serialize_response(self, response): + return response.SerializeToString() + + def deserialize_response(self, serialized_response): + return stock_pb2.StockReply.FromString(serialized_response) + + def service(self, response_callback, context, control): + return _get_highest_trade_price( + response_callback, control, context.is_active) + + +class GetHighestTradePriceMessages(service.StreamUnaryTestMessages): + + def requests(self): + return [ + stock_pb2.StockRequest(symbol=SYMBOL_FORMAT % index) + for index in range(STREAM_LENGTH)] + + def verify(self, requests, response, test_case): + price = None + symbol = None + for stock_request in requests: + current_symbol = stock_request.symbol + current_price = _price(current_symbol) + if price is None or price < current_price: + price = current_price + symbol = current_symbol + test_case.assertEqual(price, response.price) + test_case.assertEqual(symbol, response.symbol) + + +class StockTestService(service.TestService): + """A corpus of test data with one method of each RPC cardinality.""" + + def name(self): + return 'Stock' + + def unary_unary_scenarios(self): + return { + 'GetLastTradePrice': ( + GetLastTradePrice(), [GetLastTradePriceMessages()]), + } + + def unary_stream_scenarios(self): + return { + 'WatchFutureTrades': ( + WatchFutureTrades(), [WatchFutureTradesMessages()]), + } + + def stream_unary_scenarios(self): + return { + 'GetHighestTradePrice': ( + GetHighestTradePrice(), [GetHighestTradePriceMessages()]) + } + + def stream_stream_scenarios(self): + return { + 'GetLastTradePriceMultiple': ( + GetLastTradePriceMultiple(), [GetLastTradePriceMultipleMessages()]), + } + + +STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py b/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py new file mode 100644 index 0000000000..858d5cf7fd --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py @@ -0,0 +1,80 @@ +# 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. + +"""Tools for creating tests of implementations of the Face layer.""" + +import abc + +# face_interfaces and interfaces are referenced in specification in this module. +from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import +from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import + + +class FaceTestCase(object): + """Describes a test of the Face Layer of RPC Framework. + + Concrete subclasses must also inherit from unittest.TestCase and from at least + one class that defines test methods. + """ + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def set_up_implementation( + self, name, methods, method_implementations, + multi_method_implementation): + """Instantiates the Face Layer implementation under test. + + Args: + name: The service name to be used in the test. + methods: A sequence of interfaces.Method objects describing the RPC + methods that will be called during the test. + method_implementations: A dictionary from string RPC method name to + face_interfaces.MethodImplementation object specifying + implementation of an RPC method. + multi_method_implementation: An face_interfaces.MultiMethodImplementation + or None. + + Returns: + A sequence of length two the first element of which is a + face_interfaces.GenericStub (backed by the given method + implementations), and the second element of which is an arbitrary memo + object to be kept and passed to tearDownImplementation at the conclusion + of the test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def tear_down_implementation(self, memo): + """Destroys the Face layer implementation under test. + + Args: + memo: The object from the second position of the return value of + set_up_implementation. + """ + raise NotImplementedError() diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py b/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py b/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py new file mode 100644 index 0000000000..6c2459e185 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py @@ -0,0 +1,151 @@ +# 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 of the later module.""" + +import threading +import time +import unittest + +from grpc.framework.foundation import later + +TICK = 0.1 + + +class LaterTest(unittest.TestCase): + + def test_simple_delay(self): + lock = threading.Lock() + cell = [0] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + time.sleep(TICK) + self.assertFalse(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(0, cell[0]) + time.sleep(TICK * 2) + self.assertTrue(computation_future.done()) + self.assertFalse(computation_future.cancelled()) + with lock: + self.assertEqual(1, cell[0]) + self.assertEqual(return_value, computation_future.result()) + + def test_callback(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + time.sleep(TICK * 2) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + callback_called[0] = False + future_passed_to_callback[0] = None + + computation_future.add_done_callback(callback) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].done()) + + def test_cancel(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback = [None] + def computation(): + with lock: + cell[0] += 1 + computation_future = later.later(TICK * 2, computation) + def callback(outcome): + with lock: + callback_called[0] = True + future_passed_to_callback[0] = outcome + computation_future.add_done_callback(callback) + time.sleep(TICK) + with lock: + self.assertFalse(callback_called[0]) + computation_future.cancel() + self.assertTrue(computation_future.cancelled()) + self.assertFalse(computation_future.running()) + self.assertTrue(computation_future.done()) + with lock: + self.assertTrue(callback_called[0]) + self.assertTrue(future_passed_to_callback[0].cancelled()) + + def test_result(self): + lock = threading.Lock() + cell = [0] + callback_called = [False] + future_passed_to_callback_cell = [None] + return_value = object() + + def computation(): + with lock: + cell[0] += 1 + return return_value + computation_future = later.later(TICK * 2, computation) + + def callback(future_passed_to_callback): + with lock: + callback_called[0] = True + future_passed_to_callback_cell[0] = future_passed_to_callback + computation_future.add_done_callback(callback) + returned_value = computation_future.result() + self.assertEqual(return_value, returned_value) + + # The callback may not yet have been called! Sleep a tick. + time.sleep(TICK) + with lock: + self.assertTrue(callback_called[0]) + self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py b/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py new file mode 100644 index 0000000000..452802da6a --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py @@ -0,0 +1,64 @@ +# 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 grpc.framework.foundation.logging_pool.""" + +import unittest + +from grpc.framework.foundation import logging_pool + +_POOL_SIZE = 16 + + +class LoggingPoolTest(unittest.TestCase): + + def testUpAndDown(self): + pool = logging_pool.pool(_POOL_SIZE) + pool.shutdown(wait=True) + + with logging_pool.pool(_POOL_SIZE) as pool: + self.assertIsNotNone(pool) + + def testTaskExecuted(self): + test_list = [] + + with logging_pool.pool(_POOL_SIZE) as pool: + pool.submit(lambda: test_list.append(object())).result() + + self.assertTrue(test_list) + + def testException(self): + with logging_pool.pool(_POOL_SIZE) as pool: + raised_exception = pool.submit(lambda: 1/0).exception() + + self.assertIsNotNone(raised_exception) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py b/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py new file mode 100644 index 0000000000..098a53d5e7 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py @@ -0,0 +1,73 @@ +# 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. + +"""Utilities for testing stream-related code.""" + +from grpc.framework.foundation import stream + + +class TestConsumer(stream.Consumer): + """A stream.Consumer instrumented for testing. + + Attributes: + calls: A sequence of value-termination pairs describing the history of calls + made on this object. + """ + + def __init__(self): + self.calls = [] + + def consume(self, value): + """See stream.Consumer.consume for specification.""" + self.calls.append((value, False)) + + def terminate(self): + """See stream.Consumer.terminate for specification.""" + self.calls.append((None, True)) + + def consume_and_terminate(self, value): + """See stream.Consumer.consume_and_terminate for specification.""" + self.calls.append((value, True)) + + def is_legal(self): + """Reports whether or not a legal sequence of calls has been made.""" + terminated = False + for value, terminal in self.calls: + if terminated: + return False + elif terminal: + terminated = True + elif value is None: + return False + else: # pylint: disable=useless-else-on-loop + return True + + def values(self): + """Returns the sequence of values that have been passed to this Consumer.""" + return [value for value, _ in self.calls if value] diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py new file mode 100644 index 0000000000..7086519106 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py @@ -0,0 +1,30 @@ +# 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. + + diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py new file mode 100644 index 0000000000..26ca035c44 --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py @@ -0,0 +1,333 @@ +# 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 of the links interface of RPC Framework.""" + +# unittest is referenced from specification in this module. +import abc +import unittest # pylint: disable=unused-import + +from grpc.framework.interfaces.links import links +from grpc_test.framework.common import test_constants +from grpc_test.framework.interfaces.links import test_utilities + + +def at_least_n_payloads_received_predicate(n): + def predicate(ticket_sequence): + payload_count = 0 + for ticket in ticket_sequence: + if ticket.payload is not None: + payload_count += 1 + if n <= payload_count: + return True + else: + return False + return predicate + + +def terminated(ticket_sequence): + return ticket_sequence and ticket_sequence[-1].termination is not None + +_TRANSMISSION_GROUP = 'test.Group' +_TRANSMISSION_METHOD = 'TestMethod' + + +class TransmissionTest(object): + """Tests ticket transmission between two connected links. + + This class must be mixed into a unittest.TestCase that implements the abstract + methods it provides. + """ + __metaclass__ = abc.ABCMeta + + # This is a unittest.TestCase mix-in. + # pylint: disable=invalid-name + + @abc.abstractmethod + def create_transmitting_links(self): + """Creates two connected links for use in this test. + + Returns: + Two links.Links, the first of which will be used on the invocation side + of RPCs and the second of which will be used on the service side of + RPCs. + """ + raise NotImplementedError() + + @abc.abstractmethod + def destroy_transmitting_links(self, invocation_side_link, service_side_link): + """Destroys the two connected links created for this test. + + + Args: + invocation_side_link: The link used on the invocation side of RPCs in + this test. + service_side_link: The link used on the service side of RPCs in this + test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_initial_metadata(self): + """Creates a value for use as invocation-side initial metadata. + + Returns: + A metadata value appropriate for use as invocation-side initial metadata + or None if invocation-side initial metadata transmission is not + supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_terminal_metadata(self): + """Creates a value for use as invocation-side terminal metadata. + + Returns: + A metadata value appropriate for use as invocation-side terminal + metadata or None if invocation-side terminal metadata transmission is + not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_initial_metadata(self): + """Creates a value for use as service-side initial metadata. + + Returns: + A metadata value appropriate for use as service-side initial metadata or + None if service-side initial metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_terminal_metadata(self): + """Creates a value for use as service-side terminal metadata. + + Returns: + A metadata value appropriate for use as service-side terminal metadata or + None if service-side terminal metadata transmission is not supported by + the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_invocation_completion(self): + """Creates values for use as invocation-side code and message. + + Returns: + An invocation-side code value and an invocation-side message value. + Either or both may be None if invocation-side code and/or + invocation-side message transmission is not supported by the links + under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def create_service_completion(self): + """Creates values for use as service-side code and message. + + Returns: + A service-side code value and a service-side message value. Either or + both may be None if service-side code and/or service-side message + transmission is not supported by the links under test. + """ + raise NotImplementedError() + + @abc.abstractmethod + def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): + """Asserts that transmitted_metadata contains original_metadata. + + Args: + original_metadata: A metadata object used in this test. + transmitted_metadata: A metadata object obtained after transmission + through the system under test. + + Raises: + AssertionError: if the transmitted_metadata object does not contain + original_metadata. + """ + raise NotImplementedError() + + def group_and_method(self): + """Returns the group and method used in this test case. + + Returns: + A pair of the group and method used in this test case. + """ + return _TRANSMISSION_GROUP, _TRANSMISSION_METHOD + + def serialize_request(self, request): + """Serializes a request value used in this test case. + + Args: + request: A request value created by this test case. + + Returns: + A bytestring that is the serialization of the given request. + """ + return request + + def deserialize_request(self, serialized_request): + """Deserializes a request value used in this test case. + + Args: + serialized_request: A bytestring that is the serialization of some request + used in this test case. + + Returns: + The request value encoded by the given bytestring. + """ + return serialized_request + + def serialize_response(self, response): + """Serializes a response value used in this test case. + + Args: + response: A response value created by this test case. + + Returns: + A bytestring that is the serialization of the given response. + """ + return response + + def deserialize_response(self, serialized_response): + """Deserializes a response value used in this test case. + + Args: + serialized_response: A bytestring that is the serialization of some + response used in this test case. + + Returns: + The response value encoded by the given bytestring. + """ + return serialized_response + + def _assert_is_valid_metadata_payload_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata): + initial_metadata_seen = False + seen_payloads = [] + terminal_metadata_seen = False + + for ticket in ticket_sequence: + if ticket.initial_metadata is not None: + self.assertFalse(initial_metadata_seen) + self.assertFalse(seen_payloads) + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(initial_metadata, ticket.initial_metadata) + initial_metadata_seen = True + + if ticket.payload is not None: + self.assertFalse(terminal_metadata_seen) + seen_payloads.append(ticket.payload) + + if ticket.terminal_metadata is not None: + self.assertFalse(terminal_metadata_seen) + self.assertMetadataTransmitted(terminal_metadata, ticket.terminal_metadata) + terminal_metadata_seen = True + self.assertSequenceEqual(payloads, seen_payloads) + + def _assert_is_valid_invocation_sequence( + self, ticket_sequence, group, method, payloads, initial_metadata, + terminal_metadata, termination): + self.assertLess(0, len(ticket_sequence)) + self.assertEqual(group, ticket_sequence[0].group) + self.assertEqual(method, ticket_sequence[0].method) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertIs(termination, ticket_sequence[-1].termination) + + def _assert_is_valid_service_sequence( + self, ticket_sequence, payloads, initial_metadata, terminal_metadata, + code, message, termination): + self.assertLess(0, len(ticket_sequence)) + self._assert_is_valid_metadata_payload_sequence( + ticket_sequence, payloads, initial_metadata, terminal_metadata) + self.assertEqual(code, ticket_sequence[-1].code) + self.assertEqual(message, ticket_sequence[-1].message) + self.assertIs(termination, ticket_sequence[-1].termination) + + def setUp(self): + self._invocation_link, self._service_link = self.create_transmitting_links() + self._invocation_mate = test_utilities.RecordingLink() + self._service_mate = test_utilities.RecordingLink() + self._invocation_link.join_link(self._invocation_mate) + self._service_link.join_link(self._service_mate) + + def tearDown(self): + self.destroy_transmitting_links(self._invocation_link, self._service_link) + + def testSimplestRoundTrip(self): + """Tests transmission of one ticket in each direction.""" + invocation_operation_id = object() + invocation_payload = b'\x07' * 1023 + timeout = test_constants.LONG_TIMEOUT + invocation_initial_metadata = self.create_invocation_initial_metadata() + invocation_terminal_metadata = self.create_invocation_terminal_metadata() + invocation_code, invocation_message = self.create_invocation_completion() + service_payload = b'\x08' * 1025 + service_initial_metadata = self.create_service_initial_metadata() + service_terminal_metadata = self.create_service_terminal_metadata() + service_code, service_message = self.create_service_completion() + + original_invocation_ticket = links.Ticket( + invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, + invocation_payload, invocation_terminal_metadata, invocation_code, + invocation_message, links.Ticket.Termination.COMPLETION) + self._invocation_link.accept_ticket(original_invocation_ticket) + + # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the + # invocation-side ticket sequence shouldn't require granting allowance for + # another payload. + self._service_mate.block_until_tickets_satisfy( + at_least_n_payloads_received_predicate(1)) + service_operation_id = self._service_mate.tickets()[0].operation_id + self._service_link.accept_ticket( + links.Ticket( + service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, + None, 1, None, None, None, None, None, None)) + + self._service_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_invocation_sequence( + self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, + (invocation_payload,), invocation_initial_metadata, + invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) + + original_service_ticket = links.Ticket( + service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, + timeout, 0, service_initial_metadata, service_payload, + service_terminal_metadata, service_code, service_message, + links.Ticket.Termination.COMPLETION) + self._service_link.accept_ticket(original_service_ticket) + self._invocation_mate.block_until_tickets_satisfy(terminated) + self._assert_is_valid_service_sequence( + self._invocation_mate.tickets(), (service_payload,), + service_initial_metadata, service_terminal_metadata, service_code, + service_message, links.Ticket.Termination.COMPLETION) diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py new file mode 100644 index 0000000000..6c2e3346aa --- /dev/null +++ b/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py @@ -0,0 +1,66 @@ +# 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. + +"""State and behavior appropriate for use in tests.""" + +import threading + +from grpc.framework.interfaces.links import links + + +class RecordingLink(links.Link): + """A Link that records every ticket passed to it.""" + + def __init__(self): + self._condition = threading.Condition() + self._tickets = [] + + def accept_ticket(self, ticket): + with self._condition: + self._tickets.append(ticket) + self._condition.notify_all() + + def join_link(self, link): + pass + + def block_until_tickets_satisfy(self, predicate): + """Blocks until the received tickets satisfy the given predicate. + + Args: + predicate: A callable that takes a sequence of tickets and returns a + boolean value. + """ + with self._condition: + while not predicate(self._tickets): + self._condition.wait() + + def tickets(self): + """Returns a copy of the list of all tickets received by this Link.""" + with self._condition: + return tuple(self._tickets) diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py index dbea0f76ae..3f57ef04e0 100644 --- a/src/python/grpcio_test/setup.py +++ b/src/python/grpcio_test/setup.py @@ -31,7 +31,7 @@ import setuptools -_PACKAGES = setuptools.find_packages('.') +_PACKAGES = setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) _PACKAGE_DIRECTORIES = { '': '.', diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json index 8bb3939fdd..426b93fe3a 100755 --- a/tools/run_tests/python_tests.json +++ b/tools/run_tests/python_tests.json @@ -1,102 +1,102 @@ [ { - "module": "grpc._adapter._c_test", + "module": "grpc_test._adapter._c_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._low_test", + "module": "grpc_test._adapter._low_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._intermediary_low_test", + "module": "grpc_test._adapter._intermediary_low_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._links_test", + "module": "grpc_test._adapter._links_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._lonely_rear_link_test", + "module": "grpc_test._adapter._lonely_rear_link_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._blocking_invocation_inline_service_test", + "module": "grpc_test._adapter._blocking_invocation_inline_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._event_invocation_synchronous_event_service_test", + "module": "grpc_test._adapter._event_invocation_synchronous_event_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._adapter._future_invocation_asynchronous_event_service_test", + "module": "grpc_test._adapter._future_invocation_asynchronous_event_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._links._lonely_invocation_link_test", + "module": "grpc_test._links._lonely_invocation_link_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc._links._transmission_test", + "module": "grpc_test._links._transmission_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.early_adopter.implementations_test", + "module": "grpc_test.early_adopter.implementations_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.base.implementations_test", + "module": "grpc_test.framework.base.implementations_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.face.blocking_invocation_inline_service_test", + "module": "grpc_test.framework.face.blocking_invocation_inline_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.face.event_invocation_synchronous_event_service_test", + "module": "grpc_test.framework.face.event_invocation_synchronous_event_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.face.future_invocation_asynchronous_event_service_test", + "module": "grpc_test.framework.face.future_invocation_asynchronous_event_service_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.foundation._later_test", + "module": "grpc_test.framework.foundation._later_test", "pythonVersions": [ "2.7" ] }, { - "module": "grpc.framework.foundation._logging_pool_test", + "module": "grpc_test.framework.foundation._logging_pool_test", "pythonVersions": [ "2.7" ] -- cgit v1.2.3 From 791cc31a4971b0b77627044ef375e78b113475fb Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Mon, 27 Jul 2015 14:22:33 -0700 Subject: Enable Python testing and coverage --- src/python/grpcio/.gitignore | 2 +- src/python/grpcio/setup.py | 4 +-- src/python/grpcio_test/.gitignore | 10 +++++++ src/python/grpcio_test/MANIFEST.in | 3 +++ src/python/grpcio_test/commands.py | 55 ++++++++++++++++++++++++++++++++++++++ src/python/grpcio_test/setup.cfg | 3 +++ src/python/grpcio_test/setup.py | 19 ++++++++++++- 7 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 src/python/grpcio_test/.gitignore create mode 100644 src/python/grpcio_test/MANIFEST.in create mode 100644 src/python/grpcio_test/commands.py create mode 100644 src/python/grpcio_test/setup.cfg (limited to 'src/python') diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore index d89f3db999..efbe1737ba 100644 --- a/src/python/grpcio/.gitignore +++ b/src/python/grpcio/.gitignore @@ -1,5 +1,5 @@ MANIFEST -grpcio.egg-info/ +*.egg-info/ build/ dist/ *.egg diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py index 1333ae0086..e408f2ace9 100644 --- a/src/python/grpcio/setup.py +++ b/src/python/grpcio/setup.py @@ -89,7 +89,7 @@ _PACKAGE_DIRECTORIES = { _INSTALL_REQUIRES = ( 'enum34==1.0.4', 'futures==2.2.0', - 'protobuf==3.0.0a3' + 'protobuf==3.0.0a3', ) _SETUP_REQUIRES = ( @@ -97,7 +97,7 @@ _SETUP_REQUIRES = ( ) + _INSTALL_REQUIRES _COMMAND_CLASS = { - 'doc': commands.SphinxDocumentation + 'doc': commands.SphinxDocumentation, } setuptools.setup( diff --git a/src/python/grpcio_test/.gitignore b/src/python/grpcio_test/.gitignore new file mode 100644 index 0000000000..218e3a15ab --- /dev/null +++ b/src/python/grpcio_test/.gitignore @@ -0,0 +1,10 @@ +MANIFEST +*.egg-info/ +build/ +dist/ +*.egg +*.egg/ +*.eggs/ +.coverage +.coverage.* +nosetests.xml diff --git a/src/python/grpcio_test/MANIFEST.in b/src/python/grpcio_test/MANIFEST.in new file mode 100644 index 0000000000..66bb3c6f17 --- /dev/null +++ b/src/python/grpcio_test/MANIFEST.in @@ -0,0 +1,3 @@ +graft grpc_interop +graft grpc_test +include commands.py diff --git a/src/python/grpcio_test/commands.py b/src/python/grpcio_test/commands.py new file mode 100644 index 0000000000..24d6241c43 --- /dev/null +++ b/src/python/grpcio_test/commands.py @@ -0,0 +1,55 @@ +# 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. + +"""Provides distutils command classes for the GRPC Python test setup process.""" + +import os +import os.path +import sys + +import setuptools + + +class RunTests(setuptools.Command): + """Command to run all tests via py.test.""" + + description = '' + user_options = [('pytest-args=', 'a', 'arguments to pass to py.test')] + + def initialize_options(self): + self.pytest_args = [] + + def finalize_options(self): + pass + + def run(self): + # We import here to ensure that setup.py has had a chance to install the + # relevant package eggs first. + import pytest + pytest.main(self.pytest_args) diff --git a/src/python/grpcio_test/setup.cfg b/src/python/grpcio_test/setup.cfg new file mode 100644 index 0000000000..b32d3f5972 --- /dev/null +++ b/src/python/grpcio_test/setup.cfg @@ -0,0 +1,3 @@ +[pytest] +norecursedirs = _cython +python_files = *_test.py diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py index 3f57ef04e0..798710393d 100644 --- a/src/python/grpcio_test/setup.py +++ b/src/python/grpcio_test/setup.py @@ -29,8 +29,17 @@ """A setup module for the GRPC Python interop testing package.""" +import os +import os.path + import setuptools +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our commands module. +import commands + _PACKAGES = setuptools.find_packages('.', exclude=['*._cython', '*._cython.*']) _PACKAGE_DIRECTORIES = { @@ -51,5 +60,13 @@ setuptools.setup( packages=_PACKAGES, package_dir=_PACKAGE_DIRECTORIES, package_data=_PACKAGE_DATA, - install_requires=_INSTALL_REQUIRES + install_requires=_INSTALL_REQUIRES, + setup_requires=( + 'pytest>=2.6', + 'pytest-cov>=2.0', + 'pytest-xdist>=1.11', + ), + cmdclass={ + 'test': commands.RunTests + } ) -- cgit v1.2.3 From 2b84162bd4a515926635eb558da0207150590874 Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Tue, 28 Jul 2015 17:39:02 -0700 Subject: Use py.test from run_tests.py Also updates the version of grpcio_test. Now that grpcio and its tests are in different project packages, the version numbers should be kept in sync. --- .gitignore | 3 + src/python/grpcio/MANIFEST.in | 1 + src/python/grpcio/requirements.txt | 3 + src/python/grpcio_test/MANIFEST.in | 1 + src/python/grpcio_test/commands.py | 4 +- src/python/grpcio_test/requirements.txt | 5 ++ src/python/grpcio_test/setup.py | 29 +++++--- src/python/requirements.txt | 3 - tools/run_tests/build_python.sh | 21 ++++-- tools/run_tests/python_tests.json | 122 -------------------------------- tools/run_tests/run_python.sh | 9 +-- tools/run_tests/run_tests.py | 40 +++-------- 12 files changed, 63 insertions(+), 178 deletions(-) create mode 100644 src/python/grpcio/requirements.txt create mode 100644 src/python/grpcio_test/requirements.txt delete mode 100644 src/python/requirements.txt delete mode 100755 tools/run_tests/python_tests.json (limited to 'src/python') diff --git a/.gitignore b/.gitignore index 89bd1003f7..f5ca501db2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ coverage # Makefile's cache cache.mk + +# Temporary test reports +report.xml diff --git a/src/python/grpcio/MANIFEST.in b/src/python/grpcio/MANIFEST.in index 498b55f20a..9583dc7768 100644 --- a/src/python/grpcio/MANIFEST.in +++ b/src/python/grpcio/MANIFEST.in @@ -1,2 +1,3 @@ graft grpc include commands.py +include requirements.txt diff --git a/src/python/grpcio/requirements.txt b/src/python/grpcio/requirements.txt new file mode 100644 index 0000000000..43395df03b --- /dev/null +++ b/src/python/grpcio/requirements.txt @@ -0,0 +1,3 @@ +enum34==1.0.4 +futures==2.2.0 +protobuf==3.0.0a3 diff --git a/src/python/grpcio_test/MANIFEST.in b/src/python/grpcio_test/MANIFEST.in index 66bb3c6f17..c9327307dc 100644 --- a/src/python/grpcio_test/MANIFEST.in +++ b/src/python/grpcio_test/MANIFEST.in @@ -1,3 +1,4 @@ graft grpc_interop graft grpc_test include commands.py +include requirements.txt diff --git a/src/python/grpcio_test/commands.py b/src/python/grpcio_test/commands.py index 24d6241c43..c796d94c76 100644 --- a/src/python/grpcio_test/commands.py +++ b/src/python/grpcio_test/commands.py @@ -52,4 +52,6 @@ class RunTests(setuptools.Command): # We import here to ensure that setup.py has had a chance to install the # relevant package eggs first. import pytest - pytest.main(self.pytest_args) + result = pytest.main(self.pytest_args) + if result != 0: + raise SystemExit(result) diff --git a/src/python/grpcio_test/requirements.txt b/src/python/grpcio_test/requirements.txt new file mode 100644 index 0000000000..856198def5 --- /dev/null +++ b/src/python/grpcio_test/requirements.txt @@ -0,0 +1,5 @@ +pytest>=2.6 +pytest-cov>=2.0 +pytest-xdist>=1.11 +oauth2client>=1.4.7 +grpcio>=0.10.0a0 diff --git a/src/python/grpcio_test/setup.py b/src/python/grpcio_test/setup.py index 798710393d..925c32720f 100644 --- a/src/python/grpcio_test/setup.py +++ b/src/python/grpcio_test/setup.py @@ -52,21 +52,28 @@ _PACKAGE_DATA = { 'credentials/server1.pem',] } -_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.10.0a0'] +_SETUP_REQUIRES = ( + 'pytest>=2.6', + 'pytest-cov>=2.0', + 'pytest-xdist>=1.11', +) + +_INSTALL_REQUIRES = ( + 'oauth2client>=1.4.7', + 'grpcio>=0.10.0a0', +) + +_COMMAND_CLASS = { + 'test': commands.RunTests +} setuptools.setup( name='grpcio_test', - version='0.0.1', + version='0.10.0a0', packages=_PACKAGES, package_dir=_PACKAGE_DIRECTORIES, package_data=_PACKAGE_DATA, - install_requires=_INSTALL_REQUIRES, - setup_requires=( - 'pytest>=2.6', - 'pytest-cov>=2.0', - 'pytest-xdist>=1.11', - ), - cmdclass={ - 'test': commands.RunTests - } + install_requires=_INSTALL_REQUIRES + _SETUP_REQUIRES, + setup_requires=_SETUP_REQUIRES, + cmdclass=_COMMAND_CLASS ) diff --git a/src/python/requirements.txt b/src/python/requirements.txt deleted file mode 100644 index 43395df03b..0000000000 --- a/src/python/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -enum34==1.0.4 -futures==2.2.0 -protobuf==3.0.0a3 diff --git a/tools/run_tests/build_python.sh b/tools/run_tests/build_python.sh index 265a542e3a..203c8b7720 100755 --- a/tools/run_tests/build_python.sh +++ b/tools/run_tests/build_python.sh @@ -33,7 +33,9 @@ set -ex # change to grpc repo root cd $(dirname $0)/../.. -root=`pwd` +ROOT=`pwd` +GRPCIO=$ROOT/src/python/grpcio +GRPCIO_TEST=$ROOT/src/python/grpcio_test make_virtualenv() { virtualenv_name="python"$1"_virtual_environment" @@ -42,9 +44,16 @@ make_virtualenv() { # Build the entire virtual environment virtualenv -p `which "python"$1` $virtualenv_name source $virtualenv_name/bin/activate - pip install -r src/python/requirements.txt - CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/grpcio - pip install src/python/grpcio_test + + # Install grpcio + cd $GRPCIO + pip install -r requirements.txt + CFLAGS="-I$ROOT/include -std=c89" LDFLAGS=-L$ROOT/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install $GRPCIO + + # Install grpcio_test + cd $GRPCIO_TEST + pip install -r requirements.txt + pip install $GRPCIO_TEST else source $virtualenv_name/bin/activate # Uninstall and re-install the packages we care about. Don't use @@ -53,12 +62,12 @@ make_virtualenv() { # dependency upgrades. (yes | pip uninstall grpcio) || true (yes | pip uninstall grpcio_test) || true - (CFLAGS="-I$root/include -std=c89" LDFLAGS=-L$root/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install src/python/grpcio) || ( + (CFLAGS="-I$ROOT/include -std=c89" LDFLAGS=-L$ROOT/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install $GRPCIO) || ( # Fall back to rebuilding the entire environment rm -rf $virtualenv_name make_virtualenv $1 ) - pip install src/python/grpcio_test + pip install $GRPCIO_TEST fi } diff --git a/tools/run_tests/python_tests.json b/tools/run_tests/python_tests.json deleted file mode 100755 index 426b93fe3a..0000000000 --- a/tools/run_tests/python_tests.json +++ /dev/null @@ -1,122 +0,0 @@ -[ - { - "module": "grpc_test._adapter._c_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._low_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._intermediary_low_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._links_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._lonely_rear_link_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._blocking_invocation_inline_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._event_invocation_synchronous_event_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._adapter._future_invocation_asynchronous_event_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._links._lonely_invocation_link_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test._links._transmission_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.early_adopter.implementations_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.base.implementations_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.face.blocking_invocation_inline_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.face.event_invocation_synchronous_event_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.face.future_invocation_asynchronous_event_service_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.foundation._later_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_test.framework.foundation._logging_pool_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_interop._insecure_interop_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "module": "grpc_interop._secure_interop_test", - "pythonVersions": [ - "2.7" - ] - }, - { - "file": "test/compiler/python_plugin_test.py", - "pythonVersions": [ - "2.7" - ] - } -] diff --git a/tools/run_tests/run_python.sh b/tools/run_tests/run_python.sh index 4959c0241c..5ffd4460b9 100755 --- a/tools/run_tests/run_python.sh +++ b/tools/run_tests/run_python.sh @@ -33,8 +33,9 @@ set -ex # change to grpc repo root cd $(dirname $0)/../.. -root=`pwd` -export LD_LIBRARY_PATH=$root/libs/$CONFIG -export DYLD_LIBRARY_PATH=$root/libs/$CONFIG +ROOT=`pwd` +GRPCIO_TEST=$ROOT/src/python/grpcio_test +export LD_LIBRARY_PATH=$ROOT/libs/$CONFIG +export DYLD_LIBRARY_PATH=$ROOT/libs/$CONFIG source "python"$PYVER"_virtual_environment"/bin/activate -"python"$PYVER -B $* +"python"$PYVER $GRPCIO_TEST/setup.py test -a "-n8 --cov=grpc --junitxml=./report.xml" diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 50d88e66de..fa749498d2 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -187,40 +187,18 @@ class PhpLanguage(object): class PythonLanguage(object): def __init__(self): - with open('tools/run_tests/python_tests.json') as f: - self._tests = json.load(f) - self._build_python_versions = set([ - python_version - for test in self._tests - for python_version in test['pythonVersions']]) + self._build_python_versions = ['2.7'] self._has_python_versions = [] def test_specs(self, config, travis): - job_specifications = [] - for test in self._tests: - command = None - short_name = None - if 'module' in test: - command = ['tools/run_tests/run_python.sh', '-m', test['module']] - short_name = test['module'] - elif 'file' in test: - command = ['tools/run_tests/run_python.sh', test['file']] - short_name = test['file'] - else: - raise ValueError('expected input to be a module or file to run ' - 'unittests from') - for python_version in test['pythonVersions']: - if python_version in self._has_python_versions: - environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS) - environment['PYVER'] = python_version - job_specifications.append(config.job_spec( - command, None, environ=environment, shortname=short_name)) - else: - jobset.message( - 'WARNING', - 'Could not find Python {}; skipping test'.format(python_version), - '{}\n'.format(command), do_newline=True) - return job_specifications + environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS) + environment['PYVER'] = '2.7' + return [config.job_spec( + ['tools/run_tests/run_python.sh'], + None, + environ=environment, + shortname='py.test', + )] def make_targets(self): return ['static_c', 'grpc_python_plugin', 'shared_c'] -- cgit v1.2.3 From dde9071f9b46ce2cd59040e75e81a6ecc7839ab8 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Fri, 31 Jul 2015 23:24:45 +0000 Subject: Affirm metadata transmission in a common function This introduces grpc_test.test_common for gRPC-specific test code and fixes issue 2570. --- .../grpc_test/_links/_transmission_test.py | 12 ++-- src/python/grpcio_test/grpc_test/test_common.py | 71 ++++++++++++++++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 src/python/grpcio_test/grpc_test/test_common.py (limited to 'src/python') diff --git a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py index 0531fa1d33..9cdc9620f0 100644 --- a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py +++ b/src/python/grpcio_test/grpc_test/_links/_transmission_test.py @@ -35,6 +35,7 @@ from grpc._adapter import _intermediary_low from grpc._links import invocation from grpc._links import service from grpc.framework.interfaces.links import links +from grpc_test import test_common from grpc_test._links import _proto_scenarios from grpc_test.framework.common import test_constants from grpc_test.framework.interfaces.links import test_cases @@ -94,12 +95,11 @@ class TransmissionTest(test_cases.TransmissionTest, unittest.TestCase): return _intermediary_low.Code.OK, 'An exuberant test "details" message!' def assertMetadataTransmitted(self, original_metadata, transmitted_metadata): - # we need to filter out any additional metadata added in transmitted_metadata - # since implementations are allowed to add to what is sent (in any position) - keys, _ = zip(*original_metadata) - self.assertSequenceEqual( - original_metadata, - [x for x in transmitted_metadata if x[0] in keys]) + self.assertTrue( + test_common.metadata_transmitted( + original_metadata, transmitted_metadata), + '%s erroneously transmitted as %s' % ( + original_metadata, transmitted_metadata)) class RoundTripTest(unittest.TestCase): diff --git a/src/python/grpcio_test/grpc_test/test_common.py b/src/python/grpcio_test/grpc_test/test_common.py new file mode 100644 index 0000000000..f8e1f1e43f --- /dev/null +++ b/src/python/grpcio_test/grpc_test/test_common.py @@ -0,0 +1,71 @@ +# 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. + +"""Common code used throughout tests of gRPC.""" + +import collections + + +def metadata_transmitted(original_metadata, transmitted_metadata): + """Judges whether or not metadata was acceptably transmitted. + + gRPC is allowed to insert key-value pairs into the metadata values given by + applications and to reorder key-value pairs with different keys but it is not + allowed to alter existing key-value pairs or to reorder key-value pairs with + the same key. + + Args: + original_metadata: A metadata value used in a test of gRPC. + transmitted_metadata: A metadata value corresponding to original_metadata + after having been transmitted via gRPC. + + Returns: + A boolean indicating whether transmitted_metadata accurately reflects + original_metadata after having been transmitted via gRPC. + """ + original = collections.defaultdict(list) + for key, value in original_metadata: + original[key].append(value) + transmitted = collections.defaultdict(list) + for key, value in transmitted_metadata: + transmitted[key].append(value) + + for key, values in original.iteritems(): + transmitted_values = transmitted[key] + transmitted_iterator = iter(transmitted_values) + try: + for value in values: + while True: + transmitted_value = next(transmitted_iterator) + if value == transmitted_value: + break + except StopIteration: + return False + else: + return True -- cgit v1.2.3 From 99e61645ce348df59c549011d5111cd11ed1c291 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 31 Jul 2015 17:01:47 -0700 Subject: Update wrappers, tests to new create_call() --- src/core/surface/call.c | 2 -- src/csharp/ext/grpc_csharp_ext.c | 3 ++- src/node/ext/call.cc | 5 +++-- src/php/ext/grpc/call.c | 4 ++-- src/python/grpcio/grpc/_adapter/_c/types/channel.c | 2 +- src/ruby/ext/grpc/rb_channel.c | 8 ++++---- test/core/end2end/dualstack_socket_test.c | 4 ++-- test/core/end2end/no_server_test.c | 3 ++- test/core/end2end/tests/bad_hostname.c | 4 ++-- test/core/end2end/tests/cancel_after_accept.c | 4 ++-- .../end2end/tests/cancel_after_accept_and_writes_closed.c | 4 ++-- test/core/end2end/tests/cancel_after_invoke.c | 4 ++-- test/core/end2end/tests/cancel_before_invoke.c | 4 ++-- test/core/end2end/tests/cancel_in_a_vacuum.c | 4 ++-- test/core/end2end/tests/census_simple_request.c | 4 ++-- test/core/end2end/tests/disappearing_server.c | 4 ++-- .../tests/early_server_shutdown_finishes_inflight_calls.c | 4 ++-- test/core/end2end/tests/empty_batch.c | 4 ++-- test/core/end2end/tests/graceful_server_shutdown.c | 4 ++-- test/core/end2end/tests/invoke_large_request.c | 4 ++-- test/core/end2end/tests/max_concurrent_streams.c | 12 ++++++------ test/core/end2end/tests/max_message_length.c | 4 ++-- test/core/end2end/tests/ping_pong_streaming.c | 4 ++-- test/core/end2end/tests/registered_call.c | 3 ++- .../request_response_with_binary_metadata_and_payload.c | 4 ++-- .../tests/request_response_with_metadata_and_payload.c | 4 ++-- test/core/end2end/tests/request_response_with_payload.c | 4 ++-- .../tests/request_response_with_payload_and_call_creds.c | 8 ++++---- .../request_response_with_trailing_metadata_and_payload.c | 4 ++-- test/core/end2end/tests/request_with_compressed_payload.c | 4 ++-- test/core/end2end/tests/request_with_flags.c | 4 ++-- test/core/end2end/tests/request_with_large_metadata.c | 4 ++-- test/core/end2end/tests/request_with_payload.c | 4 ++-- test/core/end2end/tests/server_finishes_request.c | 4 ++-- test/core/end2end/tests/simple_delayed_request.c | 4 ++-- test/core/end2end/tests/simple_request.c | 4 ++-- .../tests/simple_request_with_high_initial_sequence_number.c | 4 ++-- test/core/fling/client.c | 12 ++++++------ test/core/surface/lame_client_test.c | 3 ++- 39 files changed, 88 insertions(+), 85 deletions(-) (limited to 'src/python') diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 5154ea5d65..e0496df5c6 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -331,8 +331,6 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, NULL); } /* cancellation is done last */ - } else { - GPR_ASSERT(inheritance_mask == 0); } for (i = 0; i < GRPC_IOREQ_OP_COUNT; i++) { call->request_set[i] = REQSET_EMPTY; diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 49a0471042..425d226bf0 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -379,7 +379,8 @@ GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq, const char *method, const char *host, gpr_timespec deadline) { - return grpc_channel_create_call(channel, cq, method, host, deadline); + return grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + method, host, deadline); } /* Channel args */ diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index dc45c8d8ae..e4451c36f6 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -511,8 +511,9 @@ NAN_METHOD(Call::New) { double deadline = args[2]->NumberValue(); grpc_channel *wrapped_channel = channel->GetWrappedChannel(); grpc_call *wrapped_call = grpc_channel_create_call( - wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method, - channel->GetHost(), MillisecondsToTimespec(deadline)); + wrapped_channel, NULL, GRPC_INHERIT_DEFAULTS, + CompletionQueueAsyncWorker::GetQueue(), *method, channel->GetHost(), + MillisecondsToTimespec(deadline)); call = new Call(wrapped_call); args.This()->SetHiddenValue(NanNew("channel_"), channel_object); } diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 1f76c7359d..26499b7615 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -240,8 +240,8 @@ PHP_METHOD(Call, __construct) { (wrapped_grpc_timeval *)zend_object_store_get_object( deadline_obj TSRMLS_CC); call->wrapped = grpc_channel_create_call( - channel->wrapped, completion_queue, method, channel->target, - deadline->wrapped); + channel->wrapped, NULL, GRPC_INHERIT_DEFAULTS, completion_queue, method, + channel->target, deadline->wrapped); } /** diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c index feb256cf00..68eaea4da6 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types/channel.c +++ b/src/python/grpcio/grpc/_adapter/_c/types/channel.c @@ -128,7 +128,7 @@ Call *pygrpc_Channel_create_call( } call = pygrpc_Call_new_empty(cq); call->c_call = grpc_channel_create_call( - self->c_chan, cq->c_cq, method, host, + self->c_chan, NULL, GRPC_INHERIT_DEFAULTS, cq->c_cq, method, host, pygrpc_cast_double_to_gpr_timespec(deadline)); return call; } diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index 0cb6fa2f80..506e7cdee7 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -212,10 +212,10 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, VALUE method, return Qnil; } - call = - grpc_channel_create_call(ch, cq, method_chars, host_chars, - grpc_rb_time_timeval(deadline, - /* absolute time */ 0)); + call = grpc_channel_create_call(ch, NULL, GRPC_INHERIT_DEFAULTS, cq, + method_chars, host_chars, + grpc_rb_time_timeval(deadline, + /* absolute time */ 0)); if (call == NULL) { rb_raise(rb_eRuntimeError, "cannot create call with method %s", method_chars); diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index 77bea2abab..e5e8df6060 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -131,8 +131,8 @@ void test_connect(const char *server_host, const char *client_host, int port, } /* Send a trivial request. */ - c = grpc_channel_create_call(client, cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(client, NULL, GRPC_INHERIT_DEFAULTS, cq, "/foo", + "foo.test.google.fr", deadline); GPR_ASSERT(c); op = ops; diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c index 6f1133c009..9e1c15e495 100644 --- a/test/core/end2end/no_server_test.c +++ b/test/core/end2end/no_server_test.c @@ -62,7 +62,8 @@ int main(int argc, char **argv) { /* create a call, channel to a non existant server */ chan = grpc_insecure_channel_create("nonexistant:54321", NULL); - call = grpc_channel_create_call(chan, cq, "/Foo", "nonexistant", deadline); + call = grpc_channel_create_call(chan, NULL, GRPC_INHERIT_DEFAULTS, cq, "/Foo", + "nonexistant", deadline); op = ops; op->op = GRPC_OP_SEND_INITIAL_METADATA; diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c index 2509ea06a0..ee4ad2d3e6 100644 --- a/test/core/end2end/tests/bad_hostname.c +++ b/test/core/end2end/tests/bad_hostname.c @@ -113,8 +113,8 @@ static void simple_request_body(grpc_end2end_test_fixture f) { char *details = NULL; size_t details_capacity = 0; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "slartibartfast.local", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "slartibartfast.local", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c index 1cc6b2d147..4462e03852 100644 --- a/test/core/end2end/tests/cancel_after_accept.c +++ b/test/core/end2end/tests/cancel_after_accept.c @@ -126,8 +126,8 @@ static void test_cancel_after_accept(grpc_end2end_test_config config, grpc_raw_byte_buffer_create(&response_payload_slice, 1); int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c index 015d437543..2ca12d9151 100644 --- a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c +++ b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c @@ -126,8 +126,8 @@ static void test_cancel_after_accept_and_writes_closed( grpc_raw_byte_buffer_create(&response_payload_slice, 1); int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c index 414ec706ce..7f72fa25a5 100644 --- a/test/core/end2end/tests/cancel_after_invoke.c +++ b/test/core/end2end/tests/cancel_after_invoke.c @@ -121,8 +121,8 @@ static void test_cancel_after_invoke(grpc_end2end_test_config config, grpc_byte_buffer *request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c index 3cfe56eca2..4db2f97785 100644 --- a/test/core/end2end/tests/cancel_before_invoke.c +++ b/test/core/end2end/tests/cancel_before_invoke.c @@ -119,8 +119,8 @@ static void test_cancel_before_invoke(grpc_end2end_test_config config, grpc_byte_buffer *request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c)); diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c index 8bffc3f4d3..c75b94d81f 100644 --- a/test/core/end2end/tests/cancel_in_a_vacuum.c +++ b/test/core/end2end/tests/cancel_in_a_vacuum.c @@ -107,8 +107,8 @@ static void test_cancel_in_a_vacuum(grpc_end2end_test_config config, gpr_timespec deadline = five_seconds_time(); cq_verifier *v_client = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == mode.initiate_cancel(c)); diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c index b414755cd1..2fb5e3e329 100644 --- a/test/core/end2end/tests/census_simple_request.c +++ b/test/core/end2end/tests/census_simple_request.c @@ -111,8 +111,8 @@ static void test_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c index a5fd74ee9a..473e69cb27 100644 --- a/test/core/end2end/tests/disappearing_server.c +++ b/test/core/end2end/tests/disappearing_server.c @@ -97,8 +97,8 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f, size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f->client, f->cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f->client, NULL, GRPC_INHERIT_DEFAULTS, f->cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c index adc59b4e94..74689dbb3e 100644 --- a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c +++ b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c @@ -105,8 +105,8 @@ static void test_early_server_shutdown_finishes_inflight_calls( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/empty_batch.c b/test/core/end2end/tests/empty_batch.c index db8458d3d9..87d0d7b844 100644 --- a/test/core/end2end/tests/empty_batch.c +++ b/test/core/end2end/tests/empty_batch.c @@ -105,8 +105,8 @@ static void empty_batch_body(grpc_end2end_test_fixture f) { cq_verifier *cqv = cq_verifier_create(f.cq); grpc_op *op = NULL; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(c, op, 0, tag(1))); diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c index 8c1889add9..f2f325d433 100644 --- a/test/core/end2end/tests/graceful_server_shutdown.c +++ b/test/core/end2end/tests/graceful_server_shutdown.c @@ -112,8 +112,8 @@ static void test_early_server_shutdown_finishes_inflight_calls( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c index 744a9ada57..060536f937 100644 --- a/test/core/end2end/tests/invoke_large_request.c +++ b/test/core/end2end/tests/invoke_large_request.c @@ -128,8 +128,8 @@ static void test_invoke_large_request(grpc_end2end_test_config config) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 1204c070af..49dd9c776d 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -113,8 +113,8 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); @@ -244,11 +244,11 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* start two requests - ensuring that the second is not accepted until the first completes */ deadline = n_seconds_time(1000); - c1 = grpc_channel_create_call(f.client, f.cq, "/alpha", - "foo.test.google.fr:1234", deadline); + c1 = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/alpha", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c1); - c2 = grpc_channel_create_call(f.client, f.cq, "/beta", - "foo.test.google.fr:1234", deadline); + c2 = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/beta", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c2); GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call( diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c index ea59a9385f..fa127374eb 100644 --- a/test/core/end2end/tests/max_message_length.c +++ b/test/core/end2end/tests/max_message_length.c @@ -128,8 +128,8 @@ static void test_max_message_length(grpc_end2end_test_config config) { f = begin_test(config, "test_max_message_length", NULL, &server_args); cqv = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", gpr_inf_future(GPR_CLOCK_REALTIME)); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c index 8a3ec96212..6efaa4be4f 100644 --- a/test/core/end2end/tests/ping_pong_streaming.c +++ b/test/core/end2end/tests/ping_pong_streaming.c @@ -124,8 +124,8 @@ static void test_pingpong_streaming(grpc_end2end_test_config config, gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world"); gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you"); - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/registered_call.c b/test/core/end2end/tests/registered_call.c index f44fd3a224..2d5999edf2 100644 --- a/test/core/end2end/tests/registered_call.c +++ b/test/core/end2end/tests/registered_call.c @@ -115,7 +115,8 @@ static void simple_request_body(grpc_end2end_test_fixture f, void *rc) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_registered_call(f.client, f.cq, rc, deadline); + c = grpc_channel_create_registered_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, + f.cq, rc, deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c index 8b8a11babe..c2e18500ad 100644 --- a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c @@ -143,8 +143,8 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c index 9821d7852f..c0b6d14e8d 100644 --- a/test/core/end2end/tests/request_response_with_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c @@ -129,8 +129,8 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_response_with_payload.c b/test/core/end2end/tests/request_response_with_payload.c index 38d3432f94..7c3b004410 100644 --- a/test/core/end2end/tests/request_response_with_payload.c +++ b/test/core/end2end/tests/request_response_with_payload.c @@ -121,8 +121,8 @@ static void request_response_with_payload(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c index b5c743b405..9337b88ee4 100644 --- a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c +++ b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c @@ -130,8 +130,8 @@ static void test_call_creds_failure(grpc_end2end_test_config config) { grpc_end2end_test_fixture f = begin_test(config, "test_call_creds_failure", NULL, NULL); gpr_timespec deadline = five_seconds_time(); - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); /* Try with credentials unfit to be set on a call (channel creds). */ @@ -175,8 +175,8 @@ static void request_response_with_payload_and_call_creds( grpc_credentials *creds = NULL; grpc_auth_context *s_auth_context = NULL; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); creds = grpc_iam_credentials_create(iam_token, iam_selector); GPR_ASSERT(creds != NULL); diff --git a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c index a5c0851d05..1901a864d0 100644 --- a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c @@ -131,8 +131,8 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_with_compressed_payload.c b/test/core/end2end/tests/request_with_compressed_payload.c index 2599f796d2..b6f20e84e5 100644 --- a/test/core/end2end/tests/request_with_compressed_payload.c +++ b/test/core/end2end/tests/request_with_compressed_payload.c @@ -141,8 +141,8 @@ static void request_with_payload_template( f = begin_test(config, test_name, client_args, server_args); cqv = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_with_flags.c b/test/core/end2end/tests/request_with_flags.c index 0bfedca0ab..a072d86941 100644 --- a/test/core/end2end/tests/request_with_flags.c +++ b/test/core/end2end/tests/request_with_flags.c @@ -121,8 +121,8 @@ static void test_invoke_request_with_flags( size_t details_capacity = 0; grpc_call_error expectation; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_with_large_metadata.c b/test/core/end2end/tests/request_with_large_metadata.c index ad34c69774..fb33b97972 100644 --- a/test/core/end2end/tests/request_with_large_metadata.c +++ b/test/core/end2end/tests/request_with_large_metadata.c @@ -122,8 +122,8 @@ static void test_request_with_large_metadata(grpc_end2end_test_config config) { int was_cancelled = 2; const int large_size = 64 * 1024; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); meta.key = "key"; diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c index 8db6457830..302c7e7727 100644 --- a/test/core/end2end/tests/request_with_payload.c +++ b/test/core/end2end/tests/request_with_payload.c @@ -120,8 +120,8 @@ static void test_invoke_request_with_payload(grpc_end2end_test_config config) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/server_finishes_request.c b/test/core/end2end/tests/server_finishes_request.c index 062a59aca2..a27ac5126b 100644 --- a/test/core/end2end/tests/server_finishes_request.c +++ b/test/core/end2end/tests/server_finishes_request.c @@ -115,8 +115,8 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c index 034e974256..0d18ff671e 100644 --- a/test/core/end2end/tests/simple_delayed_request.c +++ b/test/core/end2end/tests/simple_delayed_request.c @@ -107,8 +107,8 @@ static void simple_delayed_request_body(grpc_end2end_test_config config, config.init_client(f, client_args); - c = grpc_channel_create_call(f->client, f->cq, "/foo", "foo.test.google.fr", - deadline); + c = grpc_channel_create_call(f->client, NULL, GRPC_INHERIT_DEFAULTS, f->cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c index ca783afccd..d367ef539d 100644 --- a/test/core/end2end/tests/simple_request.c +++ b/test/core/end2end/tests/simple_request.c @@ -116,8 +116,8 @@ static void simple_request_body(grpc_end2end_test_fixture f) { int was_cancelled = 2; char *peer; - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); peer = grpc_call_get_peer(c); diff --git a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c index 2cd638cbb9..d5bd482bb9 100644 --- a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c +++ b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c @@ -115,8 +115,8 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, f.cq, "/foo", - "foo.test.google.fr:1234", deadline); + c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/fling/client.c b/test/core/fling/client.c index 5647a16101..efe09bb20e 100644 --- a/test/core/fling/client.c +++ b/test/core/fling/client.c @@ -89,9 +89,9 @@ static void init_ping_pong_request(void) { } static void step_ping_pong_request(void) { - call = - grpc_channel_create_call(channel, cq, "/Reflector/reflectUnary", - "localhost", gpr_inf_future(GPR_CLOCK_REALTIME)); + call = grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + "/Reflector/reflectUnary", "localhost", + gpr_inf_future(GPR_CLOCK_REALTIME)); GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call, ops, op - ops, (void *)1)); grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME)); @@ -101,9 +101,9 @@ static void step_ping_pong_request(void) { } static void init_ping_pong_stream(void) { - call = - grpc_channel_create_call(channel, cq, "/Reflector/reflectStream", - "localhost", gpr_inf_future(GPR_CLOCK_REALTIME)); + call = grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + "/Reflector/reflectStream", "localhost", + gpr_inf_future(GPR_CLOCK_REALTIME)); stream_init_op.op = GRPC_OP_SEND_INITIAL_METADATA; stream_init_op.data.send_initial_metadata.count = 0; GPR_ASSERT(GRPC_CALL_OK == diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c index 3a7a3a36ee..fbe5f04380 100644 --- a/test/core/surface/lame_client_test.c +++ b/test/core/surface/lame_client_test.c @@ -60,7 +60,8 @@ int main(int argc, char **argv) { chan = grpc_lame_client_channel_create("lampoon:national"); GPR_ASSERT(chan); cq = grpc_completion_queue_create(); - call = grpc_channel_create_call(chan, cq, "/Foo", "anywhere", + call = grpc_channel_create_call(chan, NULL, GRPC_INHERIT_DEFAULTS, cq, "/Foo", + "anywhere", GRPC_TIMEOUT_SECONDS_TO_DEADLINE(100)); GPR_ASSERT(call); cqv = cq_verifier_create(cq); -- cgit v1.2.3 From e1b0e6ee12eb13130abf8cee6dd8264b36ff536f Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 31 Jul 2015 17:07:31 -0700 Subject: s/inherit/propagate/g --- include/grpc/grpc.h | 14 +++++++------- src/core/surface/call.c | 6 +++--- src/core/surface/call.h | 2 +- src/core/surface/channel.c | 12 ++++++------ src/csharp/ext/grpc_csharp_ext.c | 2 +- src/php/ext/grpc/call.c | 2 +- src/python/grpcio/grpc/_adapter/_c/types/channel.c | 2 +- src/ruby/ext/grpc/rb_channel.c | 2 +- test/core/end2end/dualstack_socket_test.c | 4 ++-- test/core/end2end/no_server_test.c | 4 ++-- test/core/end2end/tests/bad_hostname.c | 2 +- test/core/end2end/tests/cancel_after_accept.c | 2 +- .../end2end/tests/cancel_after_accept_and_writes_closed.c | 2 +- test/core/end2end/tests/cancel_after_invoke.c | 2 +- test/core/end2end/tests/cancel_before_invoke.c | 2 +- test/core/end2end/tests/cancel_in_a_vacuum.c | 2 +- test/core/end2end/tests/census_simple_request.c | 2 +- test/core/end2end/tests/disappearing_server.c | 2 +- .../tests/early_server_shutdown_finishes_inflight_calls.c | 2 +- test/core/end2end/tests/empty_batch.c | 2 +- test/core/end2end/tests/graceful_server_shutdown.c | 2 +- test/core/end2end/tests/invoke_large_request.c | 2 +- test/core/end2end/tests/max_concurrent_streams.c | 6 +++--- test/core/end2end/tests/max_message_length.c | 2 +- test/core/end2end/tests/ping_pong_streaming.c | 2 +- test/core/end2end/tests/registered_call.c | 4 ++-- .../request_response_with_binary_metadata_and_payload.c | 2 +- .../tests/request_response_with_metadata_and_payload.c | 2 +- test/core/end2end/tests/request_response_with_payload.c | 2 +- .../tests/request_response_with_payload_and_call_creds.c | 4 ++-- .../request_response_with_trailing_metadata_and_payload.c | 2 +- test/core/end2end/tests/request_with_compressed_payload.c | 2 +- test/core/end2end/tests/request_with_flags.c | 2 +- test/core/end2end/tests/request_with_large_metadata.c | 2 +- test/core/end2end/tests/request_with_payload.c | 2 +- test/core/end2end/tests/server_finishes_request.c | 2 +- test/core/end2end/tests/simple_delayed_request.c | 2 +- test/core/end2end/tests/simple_request.c | 2 +- .../simple_request_with_high_initial_sequence_number.c | 2 +- test/core/fling/client.c | 4 ++-- test/core/surface/lame_client_test.c | 4 ++-- 41 files changed, 62 insertions(+), 62 deletions(-) (limited to 'src/python') diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index a34751f891..7d1f69966b 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -349,14 +349,14 @@ typedef struct grpc_op { } data; } grpc_op; -#define GRPC_INHERIT_DEADLINE 1 -#define GRPC_INHERIT_CENSUS_CONTEXT 2 +#define GRPC_PROPAGATE_DEADLINE 1 +#define GRPC_PROPAGATE_CENSUS_CONTEXT 2 /* TODO(ctiller): -#define GRPC_INHERIT_CANCELLATION 4 +#define GRPC_PROPAGATE_CANCELLATION 4 */ -#define GRPC_INHERIT_DEFAULTS \ - (GRPC_INHERIT_DEADLINE | GRPC_INHERIT_CENSUS_CONTEXT) +#define GRPC_PROPAGATE_DEFAULTS \ + (GRPC_PROPAGATE_DEADLINE | GRPC_PROPAGATE_CENSUS_CONTEXT) /** Initialize the grpc library. @@ -437,7 +437,7 @@ void grpc_channel_watch_connectivity_state( live through the invocation of this function. */ grpc_call *grpc_channel_create_call(grpc_channel *channel, grpc_call *parent_call, - gpr_uint32 inheritance_mask, + gpr_uint32 propagation_mask, grpc_completion_queue *completion_queue, const char *method, const char *host, gpr_timespec deadline); @@ -448,7 +448,7 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method, /** Create a call given a handle returned from grpc_channel_register_call */ grpc_call *grpc_channel_create_registered_call( - grpc_channel *channel, grpc_call *parent_call, gpr_uint32 inheritance_mask, + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, grpc_completion_queue *completion_queue, void *registered_call_handle, gpr_timespec deadline); diff --git a/src/core/surface/call.c b/src/core/surface/call.c index e0496df5c6..aa1060aebd 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -292,7 +292,7 @@ static void lock(grpc_call *call); static void unlock(grpc_call *call); grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, - gpr_uint32 inheritance_mask, + gpr_uint32 propagation_mask, grpc_completion_queue *cq, const void *server_transport_data, grpc_mdelem **add_initial_metadata, @@ -319,13 +319,13 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, GPR_ASSERT(call->is_client); GPR_ASSERT(!parent_call->is_client); - if (inheritance_mask & GRPC_INHERIT_DEADLINE) { + if (propagation_mask & GRPC_PROPAGATE_DEADLINE) { send_deadline = gpr_time_min( gpr_convert_clock_type(send_deadline, parent_call->send_deadline.clock_type), parent_call->send_deadline); } - if (inheritance_mask & GRPC_INHERIT_CENSUS_CONTEXT) { + if (propagation_mask & GRPC_PROPAGATE_CENSUS_CONTEXT) { grpc_call_context_set(call, GRPC_CONTEXT_TRACING, parent_call->context[GRPC_CONTEXT_TRACING].value, NULL); diff --git a/src/core/surface/call.h b/src/core/surface/call.h index f2cc8fa352..75bdbce980 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -86,7 +86,7 @@ typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success, void *user_data); grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, - gpr_uint32 inheritance_mask, + gpr_uint32 propagation_mask, grpc_completion_queue *cq, const void *server_transport_data, grpc_mdelem **add_initial_metadata, diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index 44b6e226c4..cb145e752e 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -146,7 +146,7 @@ char *grpc_channel_get_target(grpc_channel *channel) { } static grpc_call *grpc_channel_create_call_internal( - grpc_channel *channel, grpc_call *parent_call, gpr_uint32 inheritance_mask, + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, grpc_completion_queue *cq, grpc_mdelem *path_mdelem, grpc_mdelem *authority_mdelem, gpr_timespec deadline) { grpc_mdelem *send_metadata[2]; @@ -156,19 +156,19 @@ static grpc_call *grpc_channel_create_call_internal( send_metadata[0] = path_mdelem; send_metadata[1] = authority_mdelem; - return grpc_call_create(channel, parent_call, inheritance_mask, cq, NULL, + return grpc_call_create(channel, parent_call, propagation_mask, cq, NULL, send_metadata, GPR_ARRAY_SIZE(send_metadata), deadline); } grpc_call *grpc_channel_create_call(grpc_channel *channel, grpc_call *parent_call, - gpr_uint32 inheritance_mask, + gpr_uint32 propagation_mask, grpc_completion_queue *cq, const char *method, const char *host, gpr_timespec deadline) { return grpc_channel_create_call_internal( - channel, parent_call, inheritance_mask, cq, + channel, parent_call, propagation_mask, cq, grpc_mdelem_from_metadata_strings( channel->metadata_context, GRPC_MDSTR_REF(channel->path_string), grpc_mdstr_from_string(channel->metadata_context, method, 0)), @@ -195,12 +195,12 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method, } grpc_call *grpc_channel_create_registered_call( - grpc_channel *channel, grpc_call *parent_call, gpr_uint32 inheritance_mask, + grpc_channel *channel, grpc_call *parent_call, gpr_uint32 propagation_mask, grpc_completion_queue *completion_queue, void *registered_call_handle, gpr_timespec deadline) { registered_call *rc = registered_call_handle; return grpc_channel_create_call_internal( - channel, parent_call, inheritance_mask, completion_queue, + channel, parent_call, propagation_mask, completion_queue, GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), deadline); } diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 425d226bf0..b0612d4592 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -379,7 +379,7 @@ GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq, const char *method, const char *host, gpr_timespec deadline) { - return grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + return grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, method, host, deadline); } diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 26499b7615..01ec909b79 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -240,7 +240,7 @@ PHP_METHOD(Call, __construct) { (wrapped_grpc_timeval *)zend_object_store_get_object( deadline_obj TSRMLS_CC); call->wrapped = grpc_channel_create_call( - channel->wrapped, NULL, GRPC_INHERIT_DEFAULTS, completion_queue, method, + channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method, channel->target, deadline->wrapped); } diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c index 68eaea4da6..963104742f 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types/channel.c +++ b/src/python/grpcio/grpc/_adapter/_c/types/channel.c @@ -128,7 +128,7 @@ Call *pygrpc_Channel_create_call( } call = pygrpc_Call_new_empty(cq); call->c_call = grpc_channel_create_call( - self->c_chan, NULL, GRPC_INHERIT_DEFAULTS, cq->c_cq, method, host, + self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host, pygrpc_cast_double_to_gpr_timespec(deadline)); return call; } diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index 506e7cdee7..a0663607c2 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -212,7 +212,7 @@ static VALUE grpc_rb_channel_create_call(VALUE self, VALUE cqueue, VALUE method, return Qnil; } - call = grpc_channel_create_call(ch, NULL, GRPC_INHERIT_DEFAULTS, cq, + call = grpc_channel_create_call(ch, NULL, GRPC_PROPAGATE_DEFAULTS, cq, method_chars, host_chars, grpc_rb_time_timeval(deadline, /* absolute time */ 0)); diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index e5e8df6060..3c227b7e29 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -131,8 +131,8 @@ void test_connect(const char *server_host, const char *client_host, int port, } /* Send a trivial request. */ - c = grpc_channel_create_call(client, NULL, GRPC_INHERIT_DEFAULTS, cq, "/foo", - "foo.test.google.fr", deadline); + c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, cq, + "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); op = ops; diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c index 9e1c15e495..6a5e12325d 100644 --- a/test/core/end2end/no_server_test.c +++ b/test/core/end2end/no_server_test.c @@ -62,8 +62,8 @@ int main(int argc, char **argv) { /* create a call, channel to a non existant server */ chan = grpc_insecure_channel_create("nonexistant:54321", NULL); - call = grpc_channel_create_call(chan, NULL, GRPC_INHERIT_DEFAULTS, cq, "/Foo", - "nonexistant", deadline); + call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq, + "/Foo", "nonexistant", deadline); op = ops; op->op = GRPC_OP_SEND_INITIAL_METADATA; diff --git a/test/core/end2end/tests/bad_hostname.c b/test/core/end2end/tests/bad_hostname.c index ee4ad2d3e6..198ba46cd2 100644 --- a/test/core/end2end/tests/bad_hostname.c +++ b/test/core/end2end/tests/bad_hostname.c @@ -113,7 +113,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { char *details = NULL; size_t details_capacity = 0; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "slartibartfast.local", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c index 4462e03852..ee2637846b 100644 --- a/test/core/end2end/tests/cancel_after_accept.c +++ b/test/core/end2end/tests/cancel_after_accept.c @@ -126,7 +126,7 @@ static void test_cancel_after_accept(grpc_end2end_test_config config, grpc_raw_byte_buffer_create(&response_payload_slice, 1); int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c index 2ca12d9151..d8fe17fe8b 100644 --- a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c +++ b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c @@ -126,7 +126,7 @@ static void test_cancel_after_accept_and_writes_closed( grpc_raw_byte_buffer_create(&response_payload_slice, 1); int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c index 7f72fa25a5..ec070b7056 100644 --- a/test/core/end2end/tests/cancel_after_invoke.c +++ b/test/core/end2end/tests/cancel_after_invoke.c @@ -121,7 +121,7 @@ static void test_cancel_after_invoke(grpc_end2end_test_config config, grpc_byte_buffer *request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c index 4db2f97785..b511908b4f 100644 --- a/test/core/end2end/tests/cancel_before_invoke.c +++ b/test/core/end2end/tests/cancel_before_invoke.c @@ -119,7 +119,7 @@ static void test_cancel_before_invoke(grpc_end2end_test_config config, grpc_byte_buffer *request_payload = grpc_raw_byte_buffer_create(&request_payload_slice, 1); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c index c75b94d81f..7645410184 100644 --- a/test/core/end2end/tests/cancel_in_a_vacuum.c +++ b/test/core/end2end/tests/cancel_in_a_vacuum.c @@ -107,7 +107,7 @@ static void test_cancel_in_a_vacuum(grpc_end2end_test_config config, gpr_timespec deadline = five_seconds_time(); cq_verifier *v_client = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/census_simple_request.c b/test/core/end2end/tests/census_simple_request.c index 2fb5e3e329..2142dde921 100644 --- a/test/core/end2end/tests/census_simple_request.c +++ b/test/core/end2end/tests/census_simple_request.c @@ -111,7 +111,7 @@ static void test_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/disappearing_server.c b/test/core/end2end/tests/disappearing_server.c index 473e69cb27..94bed336e8 100644 --- a/test/core/end2end/tests/disappearing_server.c +++ b/test/core/end2end/tests/disappearing_server.c @@ -97,7 +97,7 @@ static void do_request_and_shutdown_server(grpc_end2end_test_fixture *f, size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f->client, NULL, GRPC_INHERIT_DEFAULTS, f->cq, + c = grpc_channel_create_call(f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c index 74689dbb3e..36cbe8067b 100644 --- a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c +++ b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c @@ -105,7 +105,7 @@ static void test_early_server_shutdown_finishes_inflight_calls( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/empty_batch.c b/test/core/end2end/tests/empty_batch.c index 87d0d7b844..138c6b07c5 100644 --- a/test/core/end2end/tests/empty_batch.c +++ b/test/core/end2end/tests/empty_batch.c @@ -105,7 +105,7 @@ static void empty_batch_body(grpc_end2end_test_fixture f) { cq_verifier *cqv = cq_verifier_create(f.cq); grpc_op *op = NULL; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/graceful_server_shutdown.c b/test/core/end2end/tests/graceful_server_shutdown.c index f2f325d433..23efa0304f 100644 --- a/test/core/end2end/tests/graceful_server_shutdown.c +++ b/test/core/end2end/tests/graceful_server_shutdown.c @@ -112,7 +112,7 @@ static void test_early_server_shutdown_finishes_inflight_calls( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c index 060536f937..e32645e2ba 100644 --- a/test/core/end2end/tests/invoke_large_request.c +++ b/test/core/end2end/tests/invoke_large_request.c @@ -128,7 +128,7 @@ static void test_invoke_large_request(grpc_end2end_test_config config) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c index 49dd9c776d..049c028835 100644 --- a/test/core/end2end/tests/max_concurrent_streams.c +++ b/test/core/end2end/tests/max_concurrent_streams.c @@ -113,7 +113,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); @@ -244,10 +244,10 @@ static void test_max_concurrent_streams(grpc_end2end_test_config config) { /* start two requests - ensuring that the second is not accepted until the first completes */ deadline = n_seconds_time(1000); - c1 = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c1 = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/alpha", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c1); - c2 = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c2 = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/beta", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c2); diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c index fa127374eb..feea49c68f 100644 --- a/test/core/end2end/tests/max_message_length.c +++ b/test/core/end2end/tests/max_message_length.c @@ -128,7 +128,7 @@ static void test_max_message_length(grpc_end2end_test_config config) { f = begin_test(config, "test_max_message_length", NULL, &server_args); cqv = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", gpr_inf_future(GPR_CLOCK_REALTIME)); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c index 6efaa4be4f..0ae611b518 100644 --- a/test/core/end2end/tests/ping_pong_streaming.c +++ b/test/core/end2end/tests/ping_pong_streaming.c @@ -124,7 +124,7 @@ static void test_pingpong_streaming(grpc_end2end_test_config config, gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world"); gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you"); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/registered_call.c b/test/core/end2end/tests/registered_call.c index 2d5999edf2..ffc4cd40a0 100644 --- a/test/core/end2end/tests/registered_call.c +++ b/test/core/end2end/tests/registered_call.c @@ -115,8 +115,8 @@ static void simple_request_body(grpc_end2end_test_fixture f, void *rc) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_registered_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, - f.cq, rc, deadline); + c = grpc_channel_create_registered_call( + f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, rc, deadline); GPR_ASSERT(c); grpc_metadata_array_init(&initial_metadata_recv); diff --git a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c index c2e18500ad..600642e6a7 100644 --- a/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_binary_metadata_and_payload.c @@ -143,7 +143,7 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c index c0b6d14e8d..7e8cce10f4 100644 --- a/test/core/end2end/tests/request_response_with_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c @@ -129,7 +129,7 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_response_with_payload.c b/test/core/end2end/tests/request_response_with_payload.c index 7c3b004410..55323895fb 100644 --- a/test/core/end2end/tests/request_response_with_payload.c +++ b/test/core/end2end/tests/request_response_with_payload.c @@ -121,7 +121,7 @@ static void request_response_with_payload(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c index 9337b88ee4..ba312d0d5c 100644 --- a/test/core/end2end/tests/request_response_with_payload_and_call_creds.c +++ b/test/core/end2end/tests/request_response_with_payload_and_call_creds.c @@ -130,7 +130,7 @@ static void test_call_creds_failure(grpc_end2end_test_config config) { grpc_end2end_test_fixture f = begin_test(config, "test_call_creds_failure", NULL, NULL); gpr_timespec deadline = five_seconds_time(); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); @@ -175,7 +175,7 @@ static void request_response_with_payload_and_call_creds( grpc_credentials *creds = NULL; grpc_auth_context *s_auth_context = NULL; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); creds = grpc_iam_credentials_create(iam_token, iam_selector); diff --git a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c index 1901a864d0..26be4ef643 100644 --- a/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c +++ b/test/core/end2end/tests/request_response_with_trailing_metadata_and_payload.c @@ -131,7 +131,7 @@ static void test_request_response_with_metadata_and_payload( size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_with_compressed_payload.c b/test/core/end2end/tests/request_with_compressed_payload.c index b6f20e84e5..5f7d83cb06 100644 --- a/test/core/end2end/tests/request_with_compressed_payload.c +++ b/test/core/end2end/tests/request_with_compressed_payload.c @@ -141,7 +141,7 @@ static void request_with_payload_template( f = begin_test(config, test_name, client_args, server_args); cqv = cq_verifier_create(f.cq); - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_with_flags.c b/test/core/end2end/tests/request_with_flags.c index a072d86941..37c4825c8b 100644 --- a/test/core/end2end/tests/request_with_flags.c +++ b/test/core/end2end/tests/request_with_flags.c @@ -121,7 +121,7 @@ static void test_invoke_request_with_flags( size_t details_capacity = 0; grpc_call_error expectation; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_with_large_metadata.c b/test/core/end2end/tests/request_with_large_metadata.c index fb33b97972..9ea8273ab0 100644 --- a/test/core/end2end/tests/request_with_large_metadata.c +++ b/test/core/end2end/tests/request_with_large_metadata.c @@ -122,7 +122,7 @@ static void test_request_with_large_metadata(grpc_end2end_test_config config) { int was_cancelled = 2; const int large_size = 64 * 1024; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c index 302c7e7727..6b5f173f01 100644 --- a/test/core/end2end/tests/request_with_payload.c +++ b/test/core/end2end/tests/request_with_payload.c @@ -120,7 +120,7 @@ static void test_invoke_request_with_payload(grpc_end2end_test_config config) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/server_finishes_request.c b/test/core/end2end/tests/server_finishes_request.c index a27ac5126b..661c099d93 100644 --- a/test/core/end2end/tests/server_finishes_request.c +++ b/test/core/end2end/tests/server_finishes_request.c @@ -115,7 +115,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c index 0d18ff671e..82d2bf9657 100644 --- a/test/core/end2end/tests/simple_delayed_request.c +++ b/test/core/end2end/tests/simple_delayed_request.c @@ -107,7 +107,7 @@ static void simple_delayed_request_body(grpc_end2end_test_config config, config.init_client(f, client_args); - c = grpc_channel_create_call(f->client, NULL, GRPC_INHERIT_DEFAULTS, f->cq, + c = grpc_channel_create_call(f->client, NULL, GRPC_PROPAGATE_DEFAULTS, f->cq, "/foo", "foo.test.google.fr", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c index d367ef539d..d60a63e682 100644 --- a/test/core/end2end/tests/simple_request.c +++ b/test/core/end2end/tests/simple_request.c @@ -116,7 +116,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { int was_cancelled = 2; char *peer; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c index d5bd482bb9..2244e22416 100644 --- a/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c +++ b/test/core/end2end/tests/simple_request_with_high_initial_sequence_number.c @@ -115,7 +115,7 @@ static void simple_request_body(grpc_end2end_test_fixture f) { size_t details_capacity = 0; int was_cancelled = 2; - c = grpc_channel_create_call(f.client, NULL, GRPC_INHERIT_DEFAULTS, f.cq, + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, "/foo", "foo.test.google.fr:1234", deadline); GPR_ASSERT(c); diff --git a/test/core/fling/client.c b/test/core/fling/client.c index efe09bb20e..17b737c9dd 100644 --- a/test/core/fling/client.c +++ b/test/core/fling/client.c @@ -89,7 +89,7 @@ static void init_ping_pong_request(void) { } static void step_ping_pong_request(void) { - call = grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + call = grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/Reflector/reflectUnary", "localhost", gpr_inf_future(GPR_CLOCK_REALTIME)); GPR_ASSERT(GRPC_CALL_OK == @@ -101,7 +101,7 @@ static void step_ping_pong_request(void) { } static void init_ping_pong_stream(void) { - call = grpc_channel_create_call(channel, NULL, GRPC_INHERIT_DEFAULTS, cq, + call = grpc_channel_create_call(channel, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/Reflector/reflectStream", "localhost", gpr_inf_future(GPR_CLOCK_REALTIME)); stream_init_op.op = GRPC_OP_SEND_INITIAL_METADATA; diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c index fbe5f04380..5e6c9ae15c 100644 --- a/test/core/surface/lame_client_test.c +++ b/test/core/surface/lame_client_test.c @@ -60,8 +60,8 @@ int main(int argc, char **argv) { chan = grpc_lame_client_channel_create("lampoon:national"); GPR_ASSERT(chan); cq = grpc_completion_queue_create(); - call = grpc_channel_create_call(chan, NULL, GRPC_INHERIT_DEFAULTS, cq, "/Foo", - "anywhere", + call = grpc_channel_create_call(chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq, + "/Foo", "anywhere", GRPC_TIMEOUT_SECONDS_TO_DEADLINE(100)); GPR_ASSERT(call); cqv = cq_verifier_create(cq); -- cgit v1.2.3 From c5ae3eb8d65287c1e4493523196e2abb5aa86d2e Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 3 Aug 2015 10:42:22 -0700 Subject: Rename grpc_server_add_http2_port to grpc_server_add_insecure_http2_port --- include/grpc/grpc.h | 2 +- src/core/surface/server_chttp2.c | 2 +- src/cpp/server/insecure_server_credentials.cc | 2 +- src/csharp/ext/grpc_csharp_ext.c | 2 +- src/node/ext/server.cc | 4 ++-- src/php/ext/grpc/server.c | 2 +- src/python/grpcio/grpc/_adapter/_c/types/server.c | 2 +- src/ruby/ext/grpc/rb_server.c | 3 ++- test/core/end2end/dualstack_socket_test.c | 4 ++-- test/core/end2end/fixtures/chttp2_fullstack.c | 2 +- test/core/end2end/fixtures/chttp2_fullstack_compression.c | 2 +- test/core/end2end/fixtures/chttp2_fullstack_uds_posix.c | 2 +- test/core/end2end/fixtures/chttp2_fullstack_uds_posix_with_poll.c | 2 +- test/core/end2end/fixtures/chttp2_fullstack_with_poll.c | 2 +- test/core/end2end/multiple_server_queues_test.c | 2 +- test/core/fling/server.c | 2 +- 16 files changed, 19 insertions(+), 18 deletions(-) (limited to 'src/python') diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index 616aab7a81..d9dd79ab65 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -562,7 +562,7 @@ void grpc_server_register_completion_queue(grpc_server *server, /** Add a HTTP2 over plaintext over tcp listener. Returns bound port number on success, 0 on failure. REQUIRES: server not started */ -int grpc_server_add_http2_port(grpc_server *server, const char *addr); +int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr); /** Start a server - tells all listeners to start listening */ void grpc_server_start(grpc_server *server); diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c index 78c53466b3..4ab845bc00 100644 --- a/src/core/surface/server_chttp2.c +++ b/src/core/surface/server_chttp2.c @@ -80,7 +80,7 @@ static void destroy(grpc_server *server, void *tcpp) { grpc_tcp_server_destroy(tcp, grpc_server_listener_destroy_done, server); } -int grpc_server_add_http2_port(grpc_server *server, const char *addr) { +int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { grpc_resolved_addresses *resolved = NULL; grpc_tcp_server *tcp = NULL; size_t i; diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc index aca3568e59..800cd36caa 100644 --- a/src/cpp/server/insecure_server_credentials.cc +++ b/src/cpp/server/insecure_server_credentials.cc @@ -41,7 +41,7 @@ class InsecureServerCredentialsImpl GRPC_FINAL : public ServerCredentials { public: int AddPortToServer(const grpc::string& addr, grpc_server* server) GRPC_OVERRIDE { - return grpc_server_add_http2_port(server, addr.c_str()); + return grpc_server_add_insecure_http2_port(server, addr.c_str()); } }; } // namespace diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 49a0471042..fd6416a5d8 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -715,7 +715,7 @@ grpcsharp_server_create(grpc_completion_queue *cq, GPR_EXPORT gpr_int32 GPR_CALLTYPE grpcsharp_server_add_insecure_http2_port(grpc_server *server, const char *addr) { - return grpc_server_add_http2_port(server, addr); + return grpc_server_add_insecure_http2_port(server, addr); } GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) { diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc index 04fabc871d..1dc179db3d 100644 --- a/src/node/ext/server.cc +++ b/src/node/ext/server.cc @@ -265,8 +265,8 @@ NAN_METHOD(Server::AddHttp2Port) { grpc_server_credentials *creds = creds_object->GetWrappedServerCredentials(); int port; if (creds == NULL) { - port = grpc_server_add_http2_port(server->wrapped_server, - *NanUtf8String(args[0])); + port = grpc_server_add_insecure_http2_port(server->wrapped_server, + *NanUtf8String(args[0])); } else { port = grpc_server_add_secure_http2_port(server->wrapped_server, *NanUtf8String(args[0]), diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c index 8b8d5b2f47..d58aa884ca 100644 --- a/src/php/ext/grpc/server.c +++ b/src/php/ext/grpc/server.c @@ -182,7 +182,7 @@ PHP_METHOD(Server, addHttp2Port) { "add_http2_port expects a string", 1 TSRMLS_CC); return; } - RETURN_LONG(grpc_server_add_http2_port(server->wrapped, addr)); + RETURN_LONG(grpc_server_add_insecure_http2_port(server->wrapped, addr)); } PHP_METHOD(Server, addSecureHttp2Port) { diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server.c b/src/python/grpcio/grpc/_adapter/_c/types/server.c index 2a00f34039..c2190ea672 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types/server.c +++ b/src/python/grpcio/grpc/_adapter/_c/types/server.c @@ -155,7 +155,7 @@ PyObject *pygrpc_Server_add_http2_port( 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); + port = grpc_server_add_insecure_http2_port(self->c_serv, addr); } return PyInt_FromLong(port); diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index 375a651d24..79a4ae8757 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -357,7 +357,8 @@ static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) { rb_raise(rb_eRuntimeError, "destroyed!"); return Qnil; } else if (rb_creds == Qnil) { - recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port)); + recvd_port = + grpc_server_add_insecure_http2_port(s->wrapped, StringValueCStr(port)); if (recvd_port == 0) { rb_raise(rb_eRuntimeError, "could not add port %s to server, not sure why", diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c index 77bea2abab..c98baeffda 100644 --- a/test/core/end2end/dualstack_socket_test.c +++ b/test/core/end2end/dualstack_socket_test.c @@ -96,8 +96,8 @@ void test_connect(const char *server_host, const char *client_host, int port, cq = grpc_completion_queue_create(); server = grpc_server_create(NULL); grpc_server_register_completion_queue(server, cq); - GPR_ASSERT((got_port = grpc_server_add_http2_port(server, server_hostport)) > - 0); + GPR_ASSERT((got_port = grpc_server_add_insecure_http2_port( + server, server_hostport)) > 0); if (port == 0) { port = got_port; } else { diff --git a/test/core/end2end/fixtures/chttp2_fullstack.c b/test/core/end2end/fixtures/chttp2_fullstack.c index 6647b949ba..53a6f0d7a5 100644 --- a/test/core/end2end/fixtures/chttp2_fullstack.c +++ b/test/core/end2end/fixtures/chttp2_fullstack.c @@ -84,7 +84,7 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f, } f->server = grpc_server_create(server_args); grpc_server_register_completion_queue(f->server, f->cq); - GPR_ASSERT(grpc_server_add_http2_port(f->server, ffd->localaddr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr)); grpc_server_start(f->server); } diff --git a/test/core/end2end/fixtures/chttp2_fullstack_compression.c b/test/core/end2end/fixtures/chttp2_fullstack_compression.c index f3d1fa22dc..0ee24c01b5 100644 --- a/test/core/end2end/fixtures/chttp2_fullstack_compression.c +++ b/test/core/end2end/fixtures/chttp2_fullstack_compression.c @@ -99,7 +99,7 @@ void chttp2_init_server_fullstack_compression(grpc_end2end_test_fixture *f, } f->server = grpc_server_create(ffd->server_args_compression); grpc_server_register_completion_queue(f->server, f->cq); - GPR_ASSERT(grpc_server_add_http2_port(f->server, ffd->localaddr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr)); grpc_server_start(f->server); } diff --git a/test/core/end2end/fixtures/chttp2_fullstack_uds_posix.c b/test/core/end2end/fixtures/chttp2_fullstack_uds_posix.c index 89ad7b8c2d..20afdb868e 100644 --- a/test/core/end2end/fixtures/chttp2_fullstack_uds_posix.c +++ b/test/core/end2end/fixtures/chttp2_fullstack_uds_posix.c @@ -89,7 +89,7 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f, } f->server = grpc_server_create(server_args); grpc_server_register_completion_queue(f->server, f->cq); - GPR_ASSERT(grpc_server_add_http2_port(f->server, ffd->localaddr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr)); grpc_server_start(f->server); } diff --git a/test/core/end2end/fixtures/chttp2_fullstack_uds_posix_with_poll.c b/test/core/end2end/fixtures/chttp2_fullstack_uds_posix_with_poll.c index a2ab25d886..8491ea6970 100644 --- a/test/core/end2end/fixtures/chttp2_fullstack_uds_posix_with_poll.c +++ b/test/core/end2end/fixtures/chttp2_fullstack_uds_posix_with_poll.c @@ -89,7 +89,7 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f, } f->server = grpc_server_create(server_args); grpc_server_register_completion_queue(f->server, f->cq); - GPR_ASSERT(grpc_server_add_http2_port(f->server, ffd->localaddr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr)); grpc_server_start(f->server); } diff --git a/test/core/end2end/fixtures/chttp2_fullstack_with_poll.c b/test/core/end2end/fixtures/chttp2_fullstack_with_poll.c index 93786d0943..2a4835add1 100644 --- a/test/core/end2end/fixtures/chttp2_fullstack_with_poll.c +++ b/test/core/end2end/fixtures/chttp2_fullstack_with_poll.c @@ -83,7 +83,7 @@ void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f, } f->server = grpc_server_create(server_args); grpc_server_register_completion_queue(f->server, f->cq); - GPR_ASSERT(grpc_server_add_http2_port(f->server, ffd->localaddr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(f->server, ffd->localaddr)); grpc_server_start(f->server); } diff --git a/test/core/end2end/multiple_server_queues_test.c b/test/core/end2end/multiple_server_queues_test.c index 208d42e6e7..7772d14ba5 100644 --- a/test/core/end2end/multiple_server_queues_test.c +++ b/test/core/end2end/multiple_server_queues_test.c @@ -45,7 +45,7 @@ int main(int argc, char **argv) { cq2 = grpc_completion_queue_create(); server = grpc_server_create(NULL); grpc_server_register_completion_queue(server, cq1); - grpc_server_add_http2_port(server, "[::]:0"); + grpc_server_add_insecure_http2_port(server, "[::]:0"); grpc_server_register_completion_queue(server, cq2); grpc_server_start(server); grpc_server_shutdown_and_notify(server, cq2, NULL); diff --git a/test/core/fling/server.c b/test/core/fling/server.c index 8f349044d9..f445c68178 100644 --- a/test/core/fling/server.c +++ b/test/core/fling/server.c @@ -216,7 +216,7 @@ int main(int argc, char **argv) { grpc_server_credentials_release(ssl_creds); } else { server = grpc_server_create(NULL); - GPR_ASSERT(grpc_server_add_http2_port(server, addr)); + GPR_ASSERT(grpc_server_add_insecure_http2_port(server, addr)); } grpc_server_register_completion_queue(server, cq); grpc_server_start(server); -- cgit v1.2.3 From 5c147631d31a8fcaf4447b9c7d48da55e5aaea0d Mon Sep 17 00:00:00 2001 From: Masood Malekghassemi Date: Fri, 31 Jul 2015 14:08:19 -0700 Subject: Add project metadata generation to Python --- src/python/grpcio/.gitignore | 1 + src/python/grpcio/commands.py | 26 ++++++++++++++++++++++++++ src/python/grpcio/setup.py | 2 ++ 3 files changed, 29 insertions(+) (limited to 'src/python') diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore index efbe1737ba..4c02b8d14d 100644 --- a/src/python/grpcio/.gitignore +++ b/src/python/grpcio/.gitignore @@ -6,3 +6,4 @@ dist/ *.egg/ *.eggs/ doc/ +_grpcio_metadata.py diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py index 605d9d5612..89c0fbf0f3 100644 --- a/src/python/grpcio/commands.py +++ b/src/python/grpcio/commands.py @@ -34,6 +34,7 @@ import os.path import sys import setuptools +from setuptools.command import build_py _CONF_PY_ADDENDUM = """ extensions.append('sphinx.ext.napoleon') @@ -74,3 +75,28 @@ class SphinxDocumentation(setuptools.Command): conf_file.write(_CONF_PY_ADDENDUM) sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) + +class BuildProjectMetadata(setuptools.Command): + """Command to generate project metadata in a module.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + with open('grpc/_grpcio_metadata.py', 'w') as module_file: + module_file.write('__version__ = """{}"""'.format( + self.distribution.get_version())) + + +class BuildPy(build_py.build_py): + """Custom project build command.""" + + def run(self): + self.run_command('build_project_metadata') + build_py.build_py.run(self) diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py index e408f2ace9..caa71a4f7c 100644 --- a/src/python/grpcio/setup.py +++ b/src/python/grpcio/setup.py @@ -98,6 +98,8 @@ _SETUP_REQUIRES = ( _COMMAND_CLASS = { 'doc': commands.SphinxDocumentation, + 'build_project_metadata': commands.BuildProjectMetadata, + 'build_py': commands.BuildPy, } setuptools.setup( -- cgit v1.2.3