1# Copyright 2020 The 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"""Generates grpc-prefixed packages using template renderer. 15 16To use this script, please use 3.7+ interpreter. This script is work-directory 17agnostic. A quick executable command: 18 19 python3 tools/distrib/python/grpc_prefixed/generate.py 20""" 21 22import dataclasses 23import datetime 24import logging 25import os 26import shutil 27import subprocess 28import sys 29 30import jinja2 31 32WORK_PATH = os.path.realpath(os.path.dirname(__file__)) 33LICENSE = os.path.join(WORK_PATH, '../../../../LICENSE') 34BUILD_PATH = os.path.join(WORK_PATH, 'build') 35DIST_PATH = os.path.join(WORK_PATH, 'dist') 36 37env = jinja2.Environment( 38 loader=jinja2.FileSystemLoader(os.path.join(WORK_PATH, 'templates'))) 39 40LOGGER = logging.getLogger(__name__) 41POPEN_TIMEOUT_S = datetime.timedelta(minutes=1).total_seconds() 42 43 44@dataclasses.dataclass 45class PackageMeta: 46 """Meta-info of a PyPI package.""" 47 name: str 48 name_long: str 49 destination_package: str 50 version: str = '1.0.0' 51 52 53def clean() -> None: 54 try: 55 shutil.rmtree(BUILD_PATH) 56 except FileNotFoundError: 57 pass 58 59 try: 60 shutil.rmtree(DIST_PATH) 61 except FileNotFoundError: 62 pass 63 64 65def generate_package(meta: PackageMeta) -> None: 66 # Makes package directory 67 package_path = os.path.join(BUILD_PATH, meta.name) 68 os.makedirs(package_path, exist_ok=True) 69 70 # Copy license 71 shutil.copyfile(LICENSE, os.path.join(package_path, 'LICENSE')) 72 73 # Generates source code 74 for template_name in env.list_templates(): 75 template = env.get_template(template_name) 76 with open( 77 os.path.join(package_path, 78 template_name.replace('.template', '')), 'w') as f: 79 f.write(template.render(dataclasses.asdict(meta))) 80 81 # Creates wheel 82 job = subprocess.Popen([ 83 sys.executable, 84 os.path.join(package_path, 'setup.py'), 'sdist', '--dist-dir', DIST_PATH 85 ], 86 cwd=package_path, 87 stdout=subprocess.PIPE, 88 stderr=subprocess.STDOUT) 89 outs, _ = job.communicate(timeout=POPEN_TIMEOUT_S) 90 91 # Logs result 92 if job.returncode != 0: 93 LOGGER.error('Wheel creation failed with %d', job.returncode) 94 LOGGER.error(outs) 95 else: 96 LOGGER.info('Package <%s> generated', meta.name) 97 98 99def main(): 100 clean() 101 102 generate_package( 103 PackageMeta(name='grpc', 104 name_long='gRPC Python', 105 destination_package='grpcio')) 106 107 generate_package( 108 PackageMeta(name='grpc-status', 109 name_long='gRPC Rich Error Status', 110 destination_package='grpcio-status')) 111 112 generate_package( 113 PackageMeta(name='grpc-channelz', 114 name_long='gRPC Channel Tracing', 115 destination_package='grpcio-channelz')) 116 117 generate_package( 118 PackageMeta(name='grpc-tools', 119 name_long='ProtoBuf Code Generator', 120 destination_package='grpcio-tools')) 121 122 generate_package( 123 PackageMeta(name='grpc-reflection', 124 name_long='gRPC Reflection', 125 destination_package='grpcio-reflection')) 126 127 generate_package( 128 PackageMeta(name='grpc-testing', 129 name_long='gRPC Testing Utility', 130 destination_package='grpcio-testing')) 131 132 generate_package( 133 PackageMeta(name='grpc-health-checking', 134 name_long='gRPC Health Checking', 135 destination_package='grpcio-health-checking')) 136 137 138if __name__ == "__main__": 139 logging.basicConfig(level=logging.INFO) 140 main() 141