mirror of https://github.com/grpc/grpc.git
Merge pull request #21458 from gnossen/dynamic_stubs
Enable Runtime Import of .proto Filespull/23877/head
commit
c79bef55ee
29 changed files with 1309 additions and 22 deletions
@ -0,0 +1,40 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Hello World without using protoc. |
||||
|
||||
This example parses message and service schemas directly from a |
||||
.proto file on the filesystem. |
||||
|
||||
Several APIs used in this example are in an experimental state. |
||||
""" |
||||
|
||||
from __future__ import print_function |
||||
import logging |
||||
|
||||
import grpc |
||||
import grpc.experimental |
||||
|
||||
# NOTE: The path to the .proto file must be reachable from an entry |
||||
# on sys.path. Use sys.path.insert or set the $PYTHONPATH variable to |
||||
# import from files located elsewhere on the filesystem. |
||||
|
||||
protos = grpc.protos("helloworld.proto") |
||||
services = grpc.services("helloworld.proto") |
||||
|
||||
logging.basicConfig() |
||||
|
||||
response = services.Greeter.SayHello(protos.HelloRequest(name='you'), |
||||
'localhost:50051', |
||||
insecure=True) |
||||
print("Greeter client received: " + response.message) |
@ -0,0 +1,40 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The Python implementation of the GRPC helloworld.Greeter server.""" |
||||
|
||||
from concurrent import futures |
||||
import logging |
||||
|
||||
import grpc |
||||
|
||||
protos, services = grpc.protos_and_services("helloworld.proto") |
||||
|
||||
|
||||
class Greeter(services.GreeterServicer): |
||||
|
||||
def SayHello(self, request, context): |
||||
return protos.HelloReply(message='Hello, %s!' % request.name) |
||||
|
||||
|
||||
def serve(): |
||||
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) |
||||
services.add_GreeterServicer_to_server(Greeter(), server) |
||||
server.add_insecure_port('[::]:50051') |
||||
server.start() |
||||
server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig() |
||||
serve() |
@ -0,0 +1,38 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
option java_multiple_files = true; |
||||
option java_package = "io.grpc.examples.helloworld"; |
||||
option java_outer_classname = "HelloWorldProto"; |
||||
option objc_class_prefix = "HLW"; |
||||
|
||||
package helloworld; |
||||
|
||||
// The greeting service definition. |
||||
service Greeter { |
||||
// Sends a greeting |
||||
rpc SayHello (HelloRequest) returns (HelloReply) {} |
||||
} |
||||
|
||||
// The request message containing the user's name. |
||||
message HelloRequest { |
||||
string name = 1; |
||||
} |
||||
|
||||
// The response message containing the greetings |
||||
message HelloReply { |
||||
string message = 1; |
||||
} |
@ -0,0 +1,161 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import sys |
||||
|
||||
|
||||
def _uninstalled_protos(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"Install the grpcio-tools package to use the protos function.") |
||||
|
||||
|
||||
def _uninstalled_services(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"Install the grpcio-tools package to use the services function.") |
||||
|
||||
|
||||
def _uninstalled_protos_and_services(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"Install the grpcio-tools package to use the protos_and_services function." |
||||
) |
||||
|
||||
|
||||
def _interpreter_version_protos(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"The protos function is only on available on Python 3.X interpreters.") |
||||
|
||||
|
||||
def _interpreter_version_services(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"The services function is only on available on Python 3.X interpreters." |
||||
) |
||||
|
||||
|
||||
def _interpreter_version_protos_and_services(*args, **kwargs): |
||||
raise NotImplementedError( |
||||
"The protos_and_services function is only on available on Python 3.X interpreters." |
||||
) |
||||
|
||||
|
||||
def protos(protobuf_path): # pylint: disable=unused-argument |
||||
"""Returns a module generated by the indicated .proto file. |
||||
|
||||
THIS IS AN EXPERIMENTAL API. |
||||
|
||||
Use this function to retrieve classes corresponding to message |
||||
definitions in the .proto file. |
||||
|
||||
To inspect the contents of the returned module, use the dir function. |
||||
For example: |
||||
|
||||
``` |
||||
protos = grpc.protos("foo.proto") |
||||
print(dir(protos)) |
||||
``` |
||||
|
||||
The returned module object corresponds to the _pb2.py file generated |
||||
by protoc. The path is expected to be relative to an entry on sys.path |
||||
and all transitive dependencies of the file should also be resolveable |
||||
from an entry on sys.path. |
||||
|
||||
To completely disable the machinery behind this function, set the |
||||
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true". |
||||
|
||||
Args: |
||||
protobuf_path: The path to the .proto file on the filesystem. This path |
||||
must be resolveable from an entry on sys.path and so must all of its |
||||
transitive dependencies. |
||||
|
||||
Returns: |
||||
A module object corresponding to the message code for the indicated |
||||
.proto file. Equivalent to a generated _pb2.py file. |
||||
""" |
||||
|
||||
|
||||
def services(protobuf_path): # pylint: disable=unused-argument |
||||
"""Returns a module generated by the indicated .proto file. |
||||
|
||||
THIS IS AN EXPERIMENTAL API. |
||||
|
||||
Use this function to retrieve classes and functions corresponding to |
||||
service definitions in the .proto file, including both stub and servicer |
||||
definitions. |
||||
|
||||
To inspect the contents of the returned module, use the dir function. |
||||
For example: |
||||
|
||||
``` |
||||
services = grpc.services("foo.proto") |
||||
print(dir(services)) |
||||
``` |
||||
|
||||
The returned module object corresponds to the _pb2_grpc.py file generated |
||||
by protoc. The path is expected to be relative to an entry on sys.path |
||||
and all transitive dependencies of the file should also be resolveable |
||||
from an entry on sys.path. |
||||
|
||||
To completely disable the machinery behind this function, set the |
||||
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true". |
||||
|
||||
Args: |
||||
protobuf_path: The path to the .proto file on the filesystem. This path |
||||
must be resolveable from an entry on sys.path and so must all of its |
||||
transitive dependencies. |
||||
|
||||
Returns: |
||||
A module object corresponding to the stub/service code for the indicated |
||||
.proto file. Equivalent to a generated _pb2_grpc.py file. |
||||
""" |
||||
|
||||
|
||||
def protos_and_services(protobuf_path): # pylint: disable=unused-argument |
||||
"""Returns a 2-tuple of modules corresponding to protos and services. |
||||
|
||||
THIS IS AN EXPERIMENTAL API. |
||||
|
||||
The return value of this function is equivalent to a call to protos and a |
||||
call to services. |
||||
|
||||
To completely disable the machinery behind this function, set the |
||||
GRPC_PYTHON_DISABLE_DYNAMIC_STUBS environment variable to "true". |
||||
|
||||
Args: |
||||
protobuf_path: The path to the .proto file on the filesystem. This path |
||||
must be resolveable from an entry on sys.path and so must all of its |
||||
transitive dependencies. |
||||
|
||||
Returns: |
||||
A 2-tuple of module objects corresponding to (protos(path), services(path)). |
||||
""" |
||||
|
||||
|
||||
if sys.version_info < (3, 5, 0): |
||||
protos = _interpreter_version_protos |
||||
services = _interpreter_version_services |
||||
protos_and_services = _interpreter_version_protos_and_services |
||||
else: |
||||
try: |
||||
import grpc_tools # pylint: disable=unused-import |
||||
except ImportError as e: |
||||
# NOTE: It's possible that we're encountering a transitive ImportError, so |
||||
# we check for that and re-raise if so. |
||||
if "grpc_tools" not in e.args[0]: |
||||
raise |
||||
protos = _uninstalled_protos |
||||
services = _uninstalled_services |
||||
protos_and_services = _uninstalled_protos_and_services |
||||
else: |
||||
from grpc_tools.protoc import _protos as protos # pylint: disable=unused-import |
||||
from grpc_tools.protoc import _services as services # pylint: disable=unused-import |
||||
from grpc_tools.protoc import _protos_and_services as protos_and_services # pylint: disable=unused-import |
@ -0,0 +1,118 @@ |
||||
# Copyright 2019 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Test of dynamic stub import API.""" |
||||
|
||||
import contextlib |
||||
import functools |
||||
import logging |
||||
import multiprocessing |
||||
import os |
||||
import sys |
||||
import unittest |
||||
|
||||
|
||||
@contextlib.contextmanager |
||||
def _grpc_tools_unimportable(): |
||||
original_sys_path = sys.path |
||||
sys.path = [path for path in sys.path if "grpcio_tools" not in path] |
||||
try: |
||||
import grpc_tools |
||||
except ImportError: |
||||
pass |
||||
else: |
||||
del grpc_tools |
||||
sys.path = original_sys_path |
||||
raise unittest.SkipTest("Failed to make grpc_tools unimportable.") |
||||
try: |
||||
yield |
||||
finally: |
||||
sys.path = original_sys_path |
||||
|
||||
|
||||
def _collect_errors(fn): |
||||
|
||||
@functools.wraps(fn) |
||||
def _wrapped(error_queue): |
||||
try: |
||||
fn() |
||||
except Exception as e: |
||||
error_queue.put(e) |
||||
raise |
||||
|
||||
return _wrapped |
||||
|
||||
|
||||
def _run_in_subprocess(test_case): |
||||
sys.path.insert( |
||||
0, os.path.join(os.path.realpath(os.path.dirname(__file__)), "..")) |
||||
error_queue = multiprocessing.Queue() |
||||
proc = multiprocessing.Process(target=test_case, args=(error_queue,)) |
||||
proc.start() |
||||
proc.join() |
||||
sys.path.pop(0) |
||||
if not error_queue.empty(): |
||||
raise error_queue.get() |
||||
assert proc.exitcode == 0, "Process exited with code {}".format( |
||||
proc.exitcode) |
||||
|
||||
|
||||
def _assert_unimplemented(msg_substr): |
||||
import grpc |
||||
try: |
||||
protos, services = grpc.protos_and_services( |
||||
"tests/unit/data/foo/bar.proto") |
||||
except NotImplementedError as e: |
||||
assert msg_substr in str(e), "{} was not in '{}'".format( |
||||
msg_substr, str(e)) |
||||
else: |
||||
assert False, "Did not raise NotImplementedError" |
||||
|
||||
|
||||
@_collect_errors |
||||
def _test_sunny_day(): |
||||
if sys.version_info[0] == 3: |
||||
import grpc |
||||
protos, services = grpc.protos_and_services( |
||||
os.path.join("tests", "unit", "data", "foo", "bar.proto")) |
||||
assert protos.BarMessage is not None |
||||
assert services.BarStub is not None |
||||
else: |
||||
_assert_unimplemented("Python 3") |
||||
|
||||
|
||||
@_collect_errors |
||||
def _test_grpc_tools_unimportable(): |
||||
with _grpc_tools_unimportable(): |
||||
if sys.version_info[0] == 3: |
||||
_assert_unimplemented("grpcio-tools") |
||||
else: |
||||
_assert_unimplemented("Python 3") |
||||
|
||||
|
||||
# NOTE(rbellevi): multiprocessing.Process fails to pickle function objects |
||||
# when they do not come from the "__main__" module, so this test passes |
||||
# if run directly on Windows, but not if started by the test runner. |
||||
@unittest.skipIf(os.name == "nt", "Windows multiprocessing unsupported") |
||||
class DynamicStubTest(unittest.TestCase): |
||||
|
||||
def test_sunny_day(self): |
||||
_run_in_subprocess(_test_sunny_day) |
||||
|
||||
def test_grpc_tools_unimportable(self): |
||||
_run_in_subprocess(_test_grpc_tools_unimportable) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
logging.basicConfig() |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,25 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package tests.unit.data.foo.bar; |
||||
|
||||
message BarMessage { |
||||
string a = 1; |
||||
}; |
||||
|
||||
service Bar { |
||||
rpc GetBar(BarMessage) returns (BarMessage); |
||||
}; |
@ -0,0 +1,53 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
package(default_visibility = [ |
||||
"//examples/python:__subpackages__", |
||||
"//src/python:__subpackages__", |
||||
"//tools/distrib/python/grpcio_tools:__subpackages__", |
||||
]) |
||||
|
||||
load("//bazel:cython_library.bzl", "pyx_library") |
||||
|
||||
cc_library( |
||||
name = "protoc_lib", |
||||
srcs = ["grpc_tools/main.cc"], |
||||
hdrs = ["grpc_tools/main.h"], |
||||
includes = ["."], |
||||
deps = [ |
||||
"//src/compiler:grpc_plugin_support", |
||||
"@com_google_protobuf//:protoc_lib", |
||||
], |
||||
) |
||||
|
||||
pyx_library( |
||||
name = "cyprotoc", |
||||
srcs = ["grpc_tools/_protoc_compiler.pyx"], |
||||
deps = [":protoc_lib"], |
||||
) |
||||
|
||||
py_library( |
||||
name = "grpc_tools", |
||||
srcs = [ |
||||
"grpc_tools/__init__.py", |
||||
"grpc_tools/protoc.py", |
||||
], |
||||
imports = ["."], |
||||
srcs_version = "PY2AND3", |
||||
deps = [ |
||||
":cyprotoc", |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"@com_google_protobuf//:protobuf_python", |
||||
], |
||||
) |
@ -0,0 +1,55 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
package(default_testonly = 1) |
||||
|
||||
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library") |
||||
|
||||
proto_library( |
||||
name = "simplest_proto", |
||||
testonly = True, |
||||
srcs = ["simplest.proto"], |
||||
strip_import_prefix = "/tools/distrib/python/grpcio_tools/grpc_tools/test/", |
||||
) |
||||
|
||||
proto_library( |
||||
name = "complicated_proto", |
||||
testonly = True, |
||||
srcs = ["complicated.proto"], |
||||
strip_import_prefix = "/tools/distrib/python/grpcio_tools/grpc_tools/test/", |
||||
deps = [":simplest_proto"], |
||||
) |
||||
|
||||
py_proto_library( |
||||
name = "complicated_py_pb2", |
||||
testonly = True, |
||||
deps = ["complicated_proto"], |
||||
) |
||||
|
||||
py_test( |
||||
name = "protoc_test", |
||||
srcs = ["protoc_test.py"], |
||||
data = [ |
||||
"complicated.proto", |
||||
"flawed.proto", |
||||
"simple.proto", |
||||
"simpler.proto", |
||||
"simplest.proto", |
||||
], |
||||
python_version = "PY3", |
||||
deps = [ |
||||
":complicated_py_pb2", |
||||
"//tools/distrib/python/grpcio_tools:grpc_tools", |
||||
], |
||||
) |
@ -0,0 +1,26 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package test.complicated; |
||||
|
||||
import "simplest.proto"; |
||||
|
||||
message ComplicatedMessage { |
||||
bool yes = 1; |
||||
bool no = 2; |
||||
bool why = 3; |
||||
simplest.SimplestMessage simplest_message = 4; |
||||
}; |
@ -0,0 +1,23 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
message Broken { |
||||
int32 no_field_number; |
||||
}; |
||||
|
||||
message Broken2 { |
||||
int32 no_field_number; |
||||
}; |
@ -0,0 +1,160 @@ |
||||
# Copyright 2020 The gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Tests for protoc.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import contextlib |
||||
import functools |
||||
import multiprocessing |
||||
import sys |
||||
import unittest |
||||
|
||||
|
||||
# TODO(https://github.com/grpc/grpc/issues/23847): Deduplicate this mechanism with |
||||
# the grpcio_tests module. |
||||
def _wrap_in_subprocess(error_queue, fn): |
||||
|
||||
@functools.wraps(fn) |
||||
def _wrapped(): |
||||
try: |
||||
fn() |
||||
except Exception as e: |
||||
error_queue.put(e) |
||||
raise |
||||
|
||||
return _wrapped |
||||
|
||||
|
||||
def _run_in_subprocess(test_case): |
||||
error_queue = multiprocessing.Queue() |
||||
wrapped_case = _wrap_in_subprocess(error_queue, test_case) |
||||
proc = multiprocessing.Process(target=wrapped_case) |
||||
proc.start() |
||||
proc.join() |
||||
if not error_queue.empty(): |
||||
raise error_queue.get() |
||||
assert proc.exitcode == 0, "Process exited with code {}".format( |
||||
proc.exitcode) |
||||
|
||||
|
||||
@contextlib.contextmanager |
||||
def _augmented_syspath(new_paths): |
||||
original_sys_path = sys.path |
||||
if new_paths is not None: |
||||
sys.path = list(new_paths) + sys.path |
||||
try: |
||||
yield |
||||
finally: |
||||
sys.path = original_sys_path |
||||
|
||||
|
||||
def _test_import_protos(): |
||||
from grpc_tools import protoc |
||||
with _augmented_syspath( |
||||
("tools/distrib/python/grpcio_tools/grpc_tools/test/",)): |
||||
protos = protoc._protos("simple.proto") |
||||
assert protos.SimpleMessage is not None |
||||
|
||||
|
||||
def _test_import_services(): |
||||
from grpc_tools import protoc |
||||
with _augmented_syspath( |
||||
("tools/distrib/python/grpcio_tools/grpc_tools/test/",)): |
||||
protos = protoc._protos("simple.proto") |
||||
services = protoc._services("simple.proto") |
||||
assert services.SimpleMessageServiceStub is not None |
||||
|
||||
|
||||
def _test_import_services_without_protos(): |
||||
from grpc_tools import protoc |
||||
with _augmented_syspath( |
||||
("tools/distrib/python/grpcio_tools/grpc_tools/test/",)): |
||||
services = protoc._services("simple.proto") |
||||
assert services.SimpleMessageServiceStub is not None |
||||
|
||||
|
||||
def _test_proto_module_imported_once(): |
||||
from grpc_tools import protoc |
||||
with _augmented_syspath( |
||||
("tools/distrib/python/grpcio_tools/grpc_tools/test/",)): |
||||
protos = protoc._protos("simple.proto") |
||||
services = protoc._services("simple.proto") |
||||
complicated_protos = protoc._protos("complicated.proto") |
||||
simple_message = protos.SimpleMessage() |
||||
complicated_message = complicated_protos.ComplicatedMessage() |
||||
assert (simple_message.simpler_message.simplest_message.__class__ is |
||||
complicated_message.simplest_message.__class__) |
||||
|
||||
|
||||
def _test_static_dynamic_combo(): |
||||
with _augmented_syspath( |
||||
("tools/distrib/python/grpcio_tools/grpc_tools/test/",)): |
||||
from grpc_tools import protoc |
||||
import complicated_pb2 |
||||
protos = protoc._protos("simple.proto") |
||||
static_message = complicated_pb2.ComplicatedMessage() |
||||
dynamic_message = protos.SimpleMessage() |
||||
assert (dynamic_message.simpler_message.simplest_message.__class__ is |
||||
static_message.simplest_message.__class__) |
||||
|
||||
|
||||
def _test_combined_import(): |
||||
from grpc_tools import protoc |
||||
protos, services = protoc._protos_and_services("simple.proto") |
||||
assert protos.SimpleMessage is not None |
||||
assert services.SimpleMessageServiceStub is not None |
||||
|
||||
|
||||
def _test_syntax_errors(): |
||||
from grpc_tools import protoc |
||||
try: |
||||
protos = protoc._protos("flawed.proto") |
||||
except Exception as e: |
||||
error_str = str(e) |
||||
assert "flawed.proto" in error_str |
||||
assert "17:23" in error_str |
||||
assert "21:23" in error_str |
||||
else: |
||||
assert False, "Compile error expected. None occurred." |
||||
|
||||
|
||||
class ProtocTest(unittest.TestCase): |
||||
|
||||
def test_import_protos(self): |
||||
_run_in_subprocess(_test_import_protos) |
||||
|
||||
def test_import_services(self): |
||||
_run_in_subprocess(_test_import_services) |
||||
|
||||
def test_import_services_without_protos(self): |
||||
_run_in_subprocess(_test_import_services_without_protos) |
||||
|
||||
def test_proto_module_imported_once(self): |
||||
_run_in_subprocess(_test_proto_module_imported_once) |
||||
|
||||
def test_static_dynamic_combo(self): |
||||
_run_in_subprocess(_test_static_dynamic_combo) |
||||
|
||||
def test_combined_import(self): |
||||
_run_in_subprocess(_test_combined_import) |
||||
|
||||
def test_syntax_errors(self): |
||||
_run_in_subprocess(_test_syntax_errors) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main() |
@ -0,0 +1,40 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package simple; |
||||
|
||||
import "simpler.proto"; |
||||
|
||||
message SimpleMessage { |
||||
string msg = 1; |
||||
oneof personal_or_business { |
||||
bool personal = 2; |
||||
bool business = 3; |
||||
}; |
||||
simpler.SimplerMessage simpler_message = 4; |
||||
}; |
||||
|
||||
message SimpleMessageRequest { |
||||
SimpleMessage simple_msg = 1; |
||||
}; |
||||
|
||||
message SimpleMessageResponse { |
||||
bool understood = 1; |
||||
}; |
||||
|
||||
service SimpleMessageService { |
||||
rpc Tell(SimpleMessageRequest) returns (SimpleMessageResponse); |
||||
}; |
@ -0,0 +1,25 @@ |
||||
syntax = "proto3"; |
||||
|
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
package simpler; |
||||
|
||||
import "simplest.proto"; |
||||
|
||||
message SimplerMessage { |
||||
int64 do_i_even_exist = 1; |
||||
simplest.SimplestMessage simplest_message = 2; |
||||
}; |
||||
|
@ -0,0 +1,22 @@ |
||||
// Copyright 2020 The gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package simplest; |
||||
|
||||
message SimplestMessage { |
||||
int64 i_definitely_dont_exist = 1; |
||||
}; |
||||
|
Loading…
Reference in new issue