Merge pull request #24478 from gnossen/runtime_protos_wkt

[gRPC Easy] Make Well-Known Types Available to Runtime Protos
pull/24513/head
Richard Belleville 4 years ago committed by GitHub
commit de90ff64d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      examples/python/route_guide/run_codegen.py
  2. 1
      src/python/grpcio_tests/tests/unit/BUILD.bazel
  3. 47
      src/python/grpcio_tests/tests/unit/_dynamic_stubs_test.py
  4. 28
      src/python/grpcio_tests/tests/unit/data/foo/bar_with_wkt.proto
  5. 9
      tools/distrib/python/grpcio_tools/BUILD.bazel
  6. 2
      tools/distrib/python/grpcio_tools/grpc_tools/protoc.py
  7. 94
      tools/distrib/python/grpcio_tools/grpcio_tools.bzl

@ -1,5 +1,3 @@
#!/bin/bash -x
# Copyright 2015 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -13,11 +11,14 @@
# 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.
"""Runs protoc with the gRPC plugin to generate messages and gRPC stubs."""
# Runs protoc with the gRPC plugin to generate messages and gRPC stubs
from grpc_tools import protoc
python3 -m grpc_tools.protoc \
-I ../../protos \
--python_out=. \
--grpc_python_out=. \
../../protos/route_guide.proto
protoc.main((
'',
'-I../../protos',
'--python_out=.',
'--grpc_python_out=.',
'../../protos/route_guide.proto',
))

@ -125,6 +125,7 @@ py2and3_test(
srcs = ["_dynamic_stubs_test.py"],
data = [
"data/foo/bar.proto",
"data/foo/bar_with_wkt.proto",
],
imports = ["../../"],
main = "_dynamic_stubs_test.py",

@ -21,6 +21,8 @@ import os
import sys
import unittest
_DATA_DIR = os.path.join("tests", "unit", "data")
@contextlib.contextmanager
def _grpc_tools_unimportable():
@ -53,6 +55,18 @@ def _collect_errors(fn):
return _wrapped
def _python3_check(fn):
@functools.wraps(fn)
def _wrapped():
if sys.version_info[0] == 3:
fn()
else:
_assert_unimplemented("Python 3")
return _wrapped
def _run_in_subprocess(test_case):
sys.path.insert(
0, os.path.join(os.path.realpath(os.path.dirname(__file__)), ".."))
@ -80,24 +94,30 @@ def _assert_unimplemented(msg_substr):
@_collect_errors
@_python3_check
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")
import grpc
protos, services = grpc.protos_and_services(
os.path.join(_DATA_DIR, "foo", "bar.proto"))
assert protos.BarMessage is not None
assert services.BarStub is not None
@_collect_errors
@_python3_check
def _test_well_known_types():
import grpc
protos, services = grpc.protos_and_services(
os.path.join(_DATA_DIR, "foo", "bar_with_wkt.proto"))
assert protos.BarMessage is not None
assert services.BarStub is not None
@_collect_errors
@_python3_check
def _test_grpc_tools_unimportable():
with _grpc_tools_unimportable():
if sys.version_info[0] == 3:
_assert_unimplemented("grpcio-tools")
else:
_assert_unimplemented("Python 3")
_assert_unimplemented("grpcio-tools")
# NOTE(rbellevi): multiprocessing.Process fails to pickle function objects
@ -109,6 +129,9 @@ class DynamicStubTest(unittest.TestCase):
def test_sunny_day(self):
_run_in_subprocess(_test_sunny_day)
def test_well_known_types(self):
_run_in_subprocess(_test_well_known_types)
def test_grpc_tools_unimportable(self):
_run_in_subprocess(_test_grpc_tools_unimportable)

@ -0,0 +1,28 @@
// 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;
import "google/protobuf/any.proto";
message BarMessage {
string a = 1;
google.protobuf.Any b = 2;
};
service Bar {
rpc GetBar(BarMessage) returns (BarMessage);
};

@ -19,6 +19,7 @@ package(default_visibility = [
])
load("//bazel:cython_library.bzl", "pyx_library")
load("grpcio_tools.bzl", "internal_copied_filegroup")
cc_library(
name = "protoc_lib",
@ -37,12 +38,20 @@ pyx_library(
deps = [":protoc_lib"],
)
internal_copied_filegroup(
name = "well_known_protos",
srcs = ["@com_google_protobuf//:well_known_protos"],
dest = "grpc_tools/_proto/",
strip_prefix = "src/",
)
py_library(
name = "grpc_tools",
srcs = [
"grpc_tools/__init__.py",
"grpc_tools/protoc.py",
],
data = [":well_known_protos"],
imports = ["."],
srcs_version = "PY2AND3",
deps = [

@ -58,6 +58,8 @@ if sys.version_info >= (3, 5, 0):
ProtoFinder(_SERVICE_MODULE_SUFFIX,
_protoc_compiler.get_services)
])
sys.path.append(
pkg_resources.resource_filename('grpc_tools', '_proto'))
_FINDERS_INSTALLED = True
def _module_name_to_proto_file(suffix, module_name):

@ -0,0 +1,94 @@
# 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.
def _generate_copied_files_impl(ctx):
srcs = ctx.attr.srcs[0]
strip_prefix = ctx.attr.strip_prefix
dest = ctx.attr.dest
outs = []
for f in srcs.files.to_list():
destination_path = f.path
if f.path.startswith("external"):
external_separator = f.path.find("/")
repository_separator = f.path.find("/", external_separator + 1)
destination_path = f.path[repository_separator + 1:]
if not destination_path.startswith(strip_prefix):
fail("File '{}' did not start with '{}'.".format(
destination_path,
strip_prefix,
))
destination_path = dest + destination_path[len(strip_prefix):]
destination_dir = destination_path.rfind("/")
out_file = ctx.actions.declare_file(destination_path)
outs.append(out_file)
ctx.actions.run_shell(
inputs = [f],
outputs = [out_file],
command = "mkdir -p {0} && cp {1} {2}".format(
out_file.dirname,
f.path,
out_file.path,
),
)
return [DefaultInfo(files = depset(direct = outs))]
_generate_copied_files = rule(
attrs = {
"srcs": attr.label_list(
mandatory = True,
allow_empty = False,
),
"strip_prefix": attr.string(
default = "",
),
"dest": attr.string(
mandatory = True,
),
},
implementation = _generate_copied_files_impl,
)
def internal_copied_filegroup(name, srcs, strip_prefix, dest):
"""Copies a file group to the current package.
Useful for using an existing filegroup as a data dependency.
Args:
name: The name of the rule.
srcs: A single filegroup.
strip_prefix: An optional string to strip from the beginning
of the path of each file in the filegroup. Must end in a slash.
dest: The directory in which to put the files, relative to the
current package. Must end in a slash.
"""
if len(srcs) != 1:
fail("srcs must be a single filegroup.")
if not dest.endswith("/"):
fail("dest must end with a '/' character.")
_symlink_target = name + "_symlink"
_generate_copied_files(
name = _symlink_target,
srcs = srcs,
strip_prefix = strip_prefix,
dest = dest,
)
native.filegroup(
name = name,
srcs = [":" + _symlink_target],
)
Loading…
Cancel
Save