├── requirements.txt ├── calculator.py ├── calculator.proto ├── client.py ├── README.md ├── LICENSE ├── server.py ├── calculator_pb2_grpc.py ├── .gitignore └── calculator_pb2.py /requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio==1.56.2 2 | grpcio-tools==1.56.2 3 | -------------------------------------------------------------------------------- /calculator.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def square_root(x): 4 | y = math.sqrt(x) 5 | return y -------------------------------------------------------------------------------- /calculator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Number { 4 | float value = 1; 5 | } 6 | 7 | service Calculator { 8 | rpc SquareRoot(Number) returns (Number) {} 9 | } -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | 3 | # import the generated classes 4 | import calculator_pb2 5 | import calculator_pb2_grpc 6 | 7 | # open a gRPC channel 8 | channel = grpc.insecure_channel('localhost:50051') 9 | 10 | # create a stub (client) 11 | stub = calculator_pb2_grpc.CalculatorStub(channel) 12 | 13 | # create a valid request message 14 | number = calculator_pb2.Number(value=16) 15 | 16 | # make the call 17 | response = stub.SquareRoot(number) 18 | 19 | # et voilà 20 | print(response.value) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic gRPC in Python 2 | 3 | Contains a minimal working example for rolling gRPC in Python. 4 | 5 | For more details, read the accompanying [blog post](https://medium.com/engineering-semantics3/a-simplified-guide-to-grpc-in-python-6c4e25f0c506). 6 | 7 | ## Quickstart 8 | 9 | ```shell 10 | git clone https://github.com/ramananbalakrishnan/basic-grpc-python 11 | cd basic-grpc-python 12 | pip install -r requirements.txt 13 | python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. calculator.proto 14 | python server.py 15 | python client.py 16 | ``` 17 | 18 | ## File reference 19 | ``` 20 | basic-grpc-python/ 21 | ├── calculator.py # module containing a function 22 | | 23 | ├── calculator.proto # protobuf definition file 24 | | 25 | ├── calculator_pb2_grpc.py # generated class for server/client 26 | ├── calculator_pb2.py # generated class for message 27 | | 28 | ├── server.py # a server to expose the function 29 | └── client.py # a sample client 30 | ``` 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ramanan Balakrishnan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | from concurrent import futures 3 | import time 4 | 5 | # import the generated classes 6 | import calculator_pb2 7 | import calculator_pb2_grpc 8 | 9 | # import the original calculator.py 10 | import calculator 11 | 12 | # create a class to define the server functions 13 | # derived from calculator_pb2_grpc.CalculatorServicer 14 | class CalculatorServicer(calculator_pb2_grpc.CalculatorServicer): 15 | 16 | # calculator.square_root is exposed here 17 | # the request and response are of the data types 18 | # generated as calculator_pb2.Number 19 | def SquareRoot(self, request, context): 20 | response = calculator_pb2.Number() 21 | response.value = calculator.square_root(request.value) 22 | return response 23 | 24 | 25 | # create a gRPC server 26 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) 27 | 28 | # use the generated function `add_CalculatorServicer_to_server` 29 | # to add the defined class to the created server 30 | calculator_pb2_grpc.add_CalculatorServicer_to_server( 31 | CalculatorServicer(), server) 32 | 33 | # listen on port 50051 34 | print('Starting server. Listening on port 50051.') 35 | server.add_insecure_port('[::]:50051') 36 | server.start() 37 | 38 | # since server.start() will not block, 39 | # a sleep-loop is added to keep alive 40 | try: 41 | while True: 42 | time.sleep(86400) 43 | except KeyboardInterrupt: 44 | server.stop(0) -------------------------------------------------------------------------------- /calculator_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | import calculator_pb2 as calculator__pb2 5 | 6 | 7 | class CalculatorStub(object): 8 | # missing associated documentation comment in .proto file 9 | pass 10 | 11 | def __init__(self, channel): 12 | """Constructor. 13 | 14 | Args: 15 | channel: A grpc.Channel. 16 | """ 17 | self.SquareRoot = channel.unary_unary( 18 | '/Calculator/SquareRoot', 19 | request_serializer=calculator__pb2.Number.SerializeToString, 20 | response_deserializer=calculator__pb2.Number.FromString, 21 | ) 22 | 23 | 24 | class CalculatorServicer(object): 25 | # missing associated documentation comment in .proto file 26 | pass 27 | 28 | def SquareRoot(self, request, context): 29 | # missing associated documentation comment in .proto file 30 | pass 31 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 32 | context.set_details('Method not implemented!') 33 | raise NotImplementedError('Method not implemented!') 34 | 35 | 36 | def add_CalculatorServicer_to_server(servicer, server): 37 | rpc_method_handlers = { 38 | 'SquareRoot': grpc.unary_unary_rpc_method_handler( 39 | servicer.SquareRoot, 40 | request_deserializer=calculator__pb2.Number.FromString, 41 | response_serializer=calculator__pb2.Number.SerializeToString, 42 | ), 43 | } 44 | generic_handler = grpc.method_handlers_generic_handler( 45 | 'Calculator', rpc_method_handlers) 46 | server.add_generic_rpc_handlers((generic_handler,)) 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | .static_storage/ 56 | .media/ 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /calculator_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: calculator.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='calculator.proto', 20 | package='', 21 | syntax='proto3', 22 | serialized_pb=_b('\n\x10\x63\x61lculator.proto\"\x17\n\x06Number\x12\r\n\x05value\x18\x01 \x01(\x02\x32.\n\nCalculator\x12 \n\nSquareRoot\x12\x07.Number\x1a\x07.Number\"\x00\x62\x06proto3') 23 | ) 24 | 25 | 26 | 27 | 28 | _NUMBER = _descriptor.Descriptor( 29 | name='Number', 30 | full_name='Number', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | containing_type=None, 34 | fields=[ 35 | _descriptor.FieldDescriptor( 36 | name='value', full_name='Number.value', index=0, 37 | number=1, type=2, cpp_type=6, label=1, 38 | has_default_value=False, default_value=float(0), 39 | message_type=None, enum_type=None, containing_type=None, 40 | is_extension=False, extension_scope=None, 41 | options=None), 42 | ], 43 | extensions=[ 44 | ], 45 | nested_types=[], 46 | enum_types=[ 47 | ], 48 | options=None, 49 | is_extendable=False, 50 | syntax='proto3', 51 | extension_ranges=[], 52 | oneofs=[ 53 | ], 54 | serialized_start=20, 55 | serialized_end=43, 56 | ) 57 | 58 | DESCRIPTOR.message_types_by_name['Number'] = _NUMBER 59 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 60 | 61 | Number = _reflection.GeneratedProtocolMessageType('Number', (_message.Message,), dict( 62 | DESCRIPTOR = _NUMBER, 63 | __module__ = 'calculator_pb2' 64 | # @@protoc_insertion_point(class_scope:Number) 65 | )) 66 | _sym_db.RegisterMessage(Number) 67 | 68 | 69 | 70 | _CALCULATOR = _descriptor.ServiceDescriptor( 71 | name='Calculator', 72 | full_name='Calculator', 73 | file=DESCRIPTOR, 74 | index=0, 75 | options=None, 76 | serialized_start=45, 77 | serialized_end=91, 78 | methods=[ 79 | _descriptor.MethodDescriptor( 80 | name='SquareRoot', 81 | full_name='Calculator.SquareRoot', 82 | index=0, 83 | containing_service=None, 84 | input_type=_NUMBER, 85 | output_type=_NUMBER, 86 | options=None, 87 | ), 88 | ]) 89 | _sym_db.RegisterServiceDescriptor(_CALCULATOR) 90 | 91 | DESCRIPTOR.services_by_name['Calculator'] = _CALCULATOR 92 | 93 | try: 94 | # THESE ELEMENTS WILL BE DEPRECATED. 95 | # Please use the generated *_pb2_grpc.py files instead. 96 | import grpc 97 | from grpc.beta import implementations as beta_implementations 98 | from grpc.beta import interfaces as beta_interfaces 99 | from grpc.framework.common import cardinality 100 | from grpc.framework.interfaces.face import utilities as face_utilities 101 | 102 | 103 | class CalculatorStub(object): 104 | # missing associated documentation comment in .proto file 105 | pass 106 | 107 | def __init__(self, channel): 108 | """Constructor. 109 | 110 | Args: 111 | channel: A grpc.Channel. 112 | """ 113 | self.SquareRoot = channel.unary_unary( 114 | '/Calculator/SquareRoot', 115 | request_serializer=Number.SerializeToString, 116 | response_deserializer=Number.FromString, 117 | ) 118 | 119 | 120 | class CalculatorServicer(object): 121 | # missing associated documentation comment in .proto file 122 | pass 123 | 124 | def SquareRoot(self, request, context): 125 | # missing associated documentation comment in .proto file 126 | pass 127 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 128 | context.set_details('Method not implemented!') 129 | raise NotImplementedError('Method not implemented!') 130 | 131 | 132 | def add_CalculatorServicer_to_server(servicer, server): 133 | rpc_method_handlers = { 134 | 'SquareRoot': grpc.unary_unary_rpc_method_handler( 135 | servicer.SquareRoot, 136 | request_deserializer=Number.FromString, 137 | response_serializer=Number.SerializeToString, 138 | ), 139 | } 140 | generic_handler = grpc.method_handlers_generic_handler( 141 | 'Calculator', rpc_method_handlers) 142 | server.add_generic_rpc_handlers((generic_handler,)) 143 | 144 | 145 | class BetaCalculatorServicer(object): 146 | """The Beta API is deprecated for 0.15.0 and later. 147 | 148 | It is recommended to use the GA API (classes and functions in this 149 | file not marked beta) for all further purposes. This class was generated 150 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.""" 151 | # missing associated documentation comment in .proto file 152 | pass 153 | def SquareRoot(self, request, context): 154 | # missing associated documentation comment in .proto file 155 | pass 156 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED) 157 | 158 | 159 | class BetaCalculatorStub(object): 160 | """The Beta API is deprecated for 0.15.0 and later. 161 | 162 | It is recommended to use the GA API (classes and functions in this 163 | file not marked beta) for all further purposes. This class was generated 164 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0.""" 165 | # missing associated documentation comment in .proto file 166 | pass 167 | def SquareRoot(self, request, timeout, metadata=None, with_call=False, protocol_options=None): 168 | # missing associated documentation comment in .proto file 169 | pass 170 | raise NotImplementedError() 171 | SquareRoot.future = None 172 | 173 | 174 | def beta_create_Calculator_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None): 175 | """The Beta API is deprecated for 0.15.0 and later. 176 | 177 | It is recommended to use the GA API (classes and functions in this 178 | file not marked beta) for all further purposes. This function was 179 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0""" 180 | request_deserializers = { 181 | ('Calculator', 'SquareRoot'): Number.FromString, 182 | } 183 | response_serializers = { 184 | ('Calculator', 'SquareRoot'): Number.SerializeToString, 185 | } 186 | method_implementations = { 187 | ('Calculator', 'SquareRoot'): face_utilities.unary_unary_inline(servicer.SquareRoot), 188 | } 189 | server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout) 190 | return beta_implementations.server(method_implementations, options=server_options) 191 | 192 | 193 | def beta_create_Calculator_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None): 194 | """The Beta API is deprecated for 0.15.0 and later. 195 | 196 | It is recommended to use the GA API (classes and functions in this 197 | file not marked beta) for all further purposes. This function was 198 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0""" 199 | request_serializers = { 200 | ('Calculator', 'SquareRoot'): Number.SerializeToString, 201 | } 202 | response_deserializers = { 203 | ('Calculator', 'SquareRoot'): Number.FromString, 204 | } 205 | cardinalities = { 206 | 'SquareRoot': cardinality.Cardinality.UNARY_UNARY, 207 | } 208 | stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size) 209 | return beta_implementations.dynamic_stub(channel, 'Calculator', cardinalities, options=stub_options) 210 | except ImportError: 211 | pass 212 | # @@protoc_insertion_point(module_scope) 213 | --------------------------------------------------------------------------------