1# Copyright 2016 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import os 16import sys 17 18from grpc_tools import protoc 19import setuptools 20 21if sys.version_info >= (3, 9, 0): 22 from importlib import resources 23else: 24 import pkg_resources 25 26 27def _get_resource_file_name( 28 package_or_requirement: str, resource_name: str 29) -> str: 30 """Obtain the filename for a resource on the file system.""" 31 file_name = None 32 if sys.version_info >= (3, 9, 0): 33 file_name = ( 34 resources.files(package_or_requirement) / resource_name 35 ).resolve() 36 else: 37 file_name = pkg_resources.resource_filename( 38 package_or_requirement, resource_name 39 ) 40 return str(file_name) 41 42 43def build_package_protos(package_root, strict_mode=False): 44 proto_files = [] 45 inclusion_root = os.path.abspath(package_root) 46 for root, _, files in os.walk(inclusion_root): 47 for filename in files: 48 if filename.endswith(".proto"): 49 proto_files.append( 50 os.path.abspath(os.path.join(root, filename)) 51 ) 52 53 well_known_protos_include = _get_resource_file_name("grpc_tools", "_proto") 54 55 for proto_file in proto_files: 56 command = [ 57 "grpc_tools.protoc", 58 "--proto_path={}".format(inclusion_root), 59 "--proto_path={}".format(well_known_protos_include), 60 "--python_out={}".format(inclusion_root), 61 "--pyi_out={}".format(inclusion_root), 62 "--grpc_python_out={}".format(inclusion_root), 63 ] + [proto_file] 64 if protoc.main(command) != 0: 65 if strict_mode: 66 raise Exception("error: {} failed".format(command)) 67 else: 68 sys.stderr.write("warning: {} failed".format(command)) 69 70 71class BuildPackageProtos(setuptools.Command): 72 """Command to generate project *_pb2.py modules from proto files.""" 73 74 description = "build grpc protobuf modules" 75 user_options = [ 76 ( 77 "strict-mode", 78 "s", 79 "exit with non-zero value if the proto compiling fails.", 80 ) 81 ] 82 83 def initialize_options(self): 84 self.strict_mode = False 85 86 def finalize_options(self): 87 pass 88 89 def run(self): 90 # due to limitations of the proto generator, we require that only *one* 91 # directory is provided as an 'include' directory. We assume it's the '' key 92 # to `self.distribution.package_dir` (and get a key error if it's not 93 # there). 94 build_package_protos( 95 self.distribution.package_dir[""], self.strict_mode 96 ) 97