blob: 8d9a40b6859de07bd88b37673e2aded0918066e6 [file] [log] [blame]
Khen Nursimulud7688092016-11-17 00:08:57 -05001#!/usr/bin/env python
2#
3# Copyright 2016 the original author or authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""protoc plugin to convert a protobuf schema to a yang schema
19
20 - basic support for message, fields. enumeration, service, method
21
22 - yang semantic rules needs to be implemented
23
24 - to run this plugin :
25
26 $ python -m grpc.tools.protoc -I.
27 --plugin=protoc-gen-custom=./proto2yang.py --custom_out=. <proto file>.proto
28
Khen Nursimulu95b919d2016-11-18 16:20:20 -050029 - the above will produce a ietf-<proto file>.yang file formatted for yang
Khen Nursimulud7688092016-11-17 00:08:57 -050030
31 - two examples of proto that can be used in the same directory are
32 yang.proto and addressbook.proto
33
34"""
35
36import sys
37
38from jinja2 import Template
39from google.protobuf.compiler import plugin_pb2 as plugin
40from descriptor_parser import DescriptorParser
41
42from google.protobuf.descriptor import FieldDescriptor
43
44template_yang = Template("""
Khen Nursimulu95b919d2016-11-18 16:20:20 -050045module ietf-{{ module.name }} {
Khen Nursimulud7688092016-11-17 00:08:57 -050046 yang-version 1.1;
Khen Nursimulu95b919d2016-11-18 16:20:20 -050047 namespace "urn:ietf:params:xml:ns:yang:ietf-{{ module.name }}";
Khen Nursimulud7688092016-11-17 00:08:57 -050048 prefix "voltha";
49
Khen Nursimulu95b919d2016-11-18 16:20:20 -050050 organization "CORD";
51 contact
52 " Any name";
53
54 description
55 "{{ module.description }}";
56
57 revision "2016-11-15" {
Khen Nursimulud7688092016-11-17 00:08:57 -050058 description "Initial revision.";
Khen Nursimulu95b919d2016-11-18 16:20:20 -050059 reference "reference";
Khen Nursimulud7688092016-11-17 00:08:57 -050060 }
Khen Nursimulubd24f552016-11-18 11:31:15 -050061
62 {% for enum in module.enums %}
Khen Nursimulubd24f552016-11-18 11:31:15 -050063 typedef {{ enum.name }} {
64 type enumeration {
65 {% for v in enum.value %}
Khen Nursimulubd24f552016-11-18 11:31:15 -050066 enum {{ v.name }} {
67 description "{{ v.description }}";
68 }
Khen Nursimulubd24f552016-11-18 11:31:15 -050069 {% endfor %}
70 }
Khen Nursimulu95b919d2016-11-18 16:20:20 -050071 description
72 "{{ enum.description }}";
Khen Nursimulubd24f552016-11-18 11:31:15 -050073 }
74 {% endfor %}
75
Khen Nursimulud7688092016-11-17 00:08:57 -050076 {% for message in module.messages recursive %}
Khen Nursimulubd24f552016-11-18 11:31:15 -050077 {% if message.name in module.referenced_messages %}
Khen Nursimulud7688092016-11-17 00:08:57 -050078 grouping {{ message.name }} {
Khen Nursimulubd24f552016-11-18 11:31:15 -050079 {% else %}
80 container {{ message.name }} {
81 {% endif %}
Khen Nursimulu95b919d2016-11-18 16:20:20 -050082 description
83 "{{ message.description }}";
Khen Nursimulud7688092016-11-17 00:08:57 -050084 {% for field in message.fields %}
85 {% if field.type_ref %}
Khen Nursimulubd24f552016-11-18 11:31:15 -050086 {% for dict_item in module.referred_messages_with_keys %}
87 {% if dict_item.name == field.type %}
88 list {{ field.name }} {
89 key "{{ dict_item.key }}";
90 {% if not field.repeated %}
91 max-elements 1;
92 {% endif %}
93 uses {{ field.type }};
Khen Nursimulu95b919d2016-11-18 16:20:20 -050094 description
95 "{{ field.description }}";
Khen Nursimulubd24f552016-11-18 11:31:15 -050096 }
97 {% endif %}
98 {% endfor %}
99 {% elif field.repeated %}
100 list {{ field.name }} {
101 key "{{ field.name }}";
102 leaf {{ field.name }} {
103 {% if field.type == "decimal64" %}
104 type {{ field.type }} {
105 fraction-digits 5;
106 }
107 {% else %}
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500108 type {{ field.type }};
Khen Nursimulubd24f552016-11-18 11:31:15 -0500109 {% endif %}
Khen Nursimulubd24f552016-11-18 11:31:15 -0500110 description
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500111 "{{ field.description }}";
Khen Nursimulubd24f552016-11-18 11:31:15 -0500112 }
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500113 description
114 "{{ field.description }}";
Khen Nursimulubd24f552016-11-18 11:31:15 -0500115 }
Khen Nursimulud7688092016-11-17 00:08:57 -0500116 {% else %}
117 leaf {{ field.name }} {
118 {% if field.type == "decimal64" %}
119 type {{ field.type }} {
120 fraction-digits 5;
121 }
122 {% else %}
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500123 type {{ field.type }};
Khen Nursimulud7688092016-11-17 00:08:57 -0500124 {% endif %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500125 description
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500126 "{{ field.description }}";
Khen Nursimulud7688092016-11-17 00:08:57 -0500127 }
128 {% endif %}
Khen Nursimulubd24f552016-11-18 11:31:15 -0500129
Khen Nursimulud7688092016-11-17 00:08:57 -0500130 {% endfor %}
131 {% for enum_type in message.enums %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500132 typedef {{ enum_type.name }} {
133 type enumeration {
134 {% for v in enum_type.value %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500135 enum {{ v.name }} {
136 description "{{ v.description }}";
137 }
Khen Nursimulud7688092016-11-17 00:08:57 -0500138 {% endfor %}
139 }
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500140 description
141 "{{ enum_type.description }}";
Khen Nursimulud7688092016-11-17 00:08:57 -0500142 }
Khen Nursimulubd24f552016-11-18 11:31:15 -0500143
Khen Nursimulud7688092016-11-17 00:08:57 -0500144 {% endfor %}
145 {% if message.messages %}
146 {{ loop (message.messages)|indent(4, false) }}
147 {% endif %}
148 }
Khen Nursimulud7688092016-11-17 00:08:57 -0500149
Khen Nursimulubd24f552016-11-18 11:31:15 -0500150 {% endfor %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500151 {% for service in module.services %}
152 {% if service.description %}
153 /* {{ service.description }}" */
154 {% endif %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500155 {% for method in service.methods %}
Khen Nursimulud7688092016-11-17 00:08:57 -0500156 rpc {{ service.service }}-{{ method.method }} {
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500157 description
158 "{{ method.description }}";
Khen Nursimulud7688092016-11-17 00:08:57 -0500159 {% if method.input %}
160 input {
161 {% if method.input_ref %}
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500162 uses {{ method.input }};
Khen Nursimulud7688092016-11-17 00:08:57 -0500163 {% else %}
164 leaf {{ method.input }} {
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500165 type {{ method.input }};
Khen Nursimulud7688092016-11-17 00:08:57 -0500166 }
167 {% endif %}
168 }
169 {% endif %}
170 {% if method.output %}
171 output {
172 {% if method.output_ref %}
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500173 uses {{ method.output }};
Khen Nursimulud7688092016-11-17 00:08:57 -0500174 {% else %}
175 leaf {{ method.output }} {
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500176 type {{ method.output }};
Khen Nursimulud7688092016-11-17 00:08:57 -0500177 }
178 {% endif %}
179 }
180 {% endif %}
181 }
Khen Nursimulubd24f552016-11-18 11:31:15 -0500182
Khen Nursimulud7688092016-11-17 00:08:57 -0500183 {% endfor %}
Khen Nursimulubd24f552016-11-18 11:31:15 -0500184
Khen Nursimulud7688092016-11-17 00:08:57 -0500185 {% endfor %}
186}
187""", trim_blocks=True, lstrip_blocks=True)
188
189
Khen Nursimulubd24f552016-11-18 11:31:15 -0500190def traverse_messages(message_types, prefix, referenced_messages):
Khen Nursimulud7688092016-11-17 00:08:57 -0500191 messages = []
192 for message_type in message_types:
193 assert message_type['_type'] == 'google.protobuf.DescriptorProto'
Khen Nursimulubd24f552016-11-18 11:31:15 -0500194
195 # full_name = prefix + '-' + message_type['name']
196 full_name = message_type['name']
197
Khen Nursimulud7688092016-11-17 00:08:57 -0500198 # parse the fields
Khen Nursimulubd24f552016-11-18 11:31:15 -0500199 fields = traverse_fields(message_type.get('field', []), full_name,
200 referenced_messages)
Khen Nursimulud7688092016-11-17 00:08:57 -0500201
202 # parse the enums
Khen Nursimulubd24f552016-11-18 11:31:15 -0500203 enums = traverse_enums(message_type.get('enum_type', []), full_name)
Khen Nursimulud7688092016-11-17 00:08:57 -0500204
205 # parse nested messages
206 nested = message_type.get('nested_type', [])
Khen Nursimulubd24f552016-11-18 11:31:15 -0500207 nested_messages = traverse_messages(nested, full_name,
208 referenced_messages)
Khen Nursimulud7688092016-11-17 00:08:57 -0500209 messages.append(
210 {
Khen Nursimulubd24f552016-11-18 11:31:15 -0500211 'name': full_name,
Khen Nursimulud7688092016-11-17 00:08:57 -0500212 'fields': fields,
213 'enums': enums,
214 # 'extensions': extensions,
215 'messages': nested_messages,
Khen Nursimulubd24f552016-11-18 11:31:15 -0500216 'description': remove_unsupported_characters(
217 message_type.get('_description', '')),
Khen Nursimulud7688092016-11-17 00:08:57 -0500218 # 'extension_ranges': extension_ranges,
219 # 'oneof': oneof
220 }
221 )
222 return messages
223
224
Khen Nursimulubd24f552016-11-18 11:31:15 -0500225def traverse_fields(fields_desc, prefix, referenced_messages):
Khen Nursimulud7688092016-11-17 00:08:57 -0500226 fields = []
227 for field in fields_desc:
228 assert field['_type'] == 'google.protobuf.FieldDescriptorProto'
Khen Nursimulubd24f552016-11-18 11:31:15 -0500229 yang_base_type = is_base_type(field['type'])
230 _type = get_yang_type(field)
231 if not yang_base_type:
232 referenced_messages.append(_type)
233
Khen Nursimulud7688092016-11-17 00:08:57 -0500234 fields.append(
235 {
Khen Nursimulubd24f552016-11-18 11:31:15 -0500236 # 'name': prefix + '-' + field.get('name', ''),
Khen Nursimulud7688092016-11-17 00:08:57 -0500237 'name': field.get('name', ''),
238 'label': field.get('label', ''),
Khen Nursimulubd24f552016-11-18 11:31:15 -0500239 'repeated': field['label'] == FieldDescriptor.LABEL_REPEATED,
Khen Nursimulud7688092016-11-17 00:08:57 -0500240 'number': field.get('number', ''),
241 'options': field.get('options', ''),
242 'type_name': field.get('type_name', ''),
Khen Nursimulubd24f552016-11-18 11:31:15 -0500243 'type': _type,
244 'type_ref': not yang_base_type,
Khen Nursimulud7688092016-11-17 00:08:57 -0500245 'description': remove_unsupported_characters(field.get(
246 '_description', ''))
247 }
248 )
249 return fields
250
251
Khen Nursimulubd24f552016-11-18 11:31:15 -0500252def traverse_enums(enums_desc, prefix):
Khen Nursimulud7688092016-11-17 00:08:57 -0500253 enums = []
254 for enum in enums_desc:
255 assert enum['_type'] == 'google.protobuf.EnumDescriptorProto'
Khen Nursimulubd24f552016-11-18 11:31:15 -0500256 # full_name = prefix + '-' + enum.get('name', '')
257 full_name = enum.get('name', '')
Khen Nursimulud7688092016-11-17 00:08:57 -0500258 enums.append(
259 {
Khen Nursimulubd24f552016-11-18 11:31:15 -0500260 'name': full_name,
Khen Nursimulud7688092016-11-17 00:08:57 -0500261 'value': enum.get('value', ''),
Khen Nursimulubd24f552016-11-18 11:31:15 -0500262 'description': remove_unsupported_characters(enum.get(
263 '_description', ''))
Khen Nursimulud7688092016-11-17 00:08:57 -0500264 }
265 )
266 return enums
267
268
Khen Nursimulubd24f552016-11-18 11:31:15 -0500269def traverse_services(service_desc, referenced_messages):
Khen Nursimulud7688092016-11-17 00:08:57 -0500270 services = []
271 for service in service_desc:
272 methods = []
273 for method in service.get('method', []):
274 assert method['_type'] == 'google.protobuf.MethodDescriptorProto'
Khen Nursimulubd24f552016-11-18 11:31:15 -0500275
Khen Nursimulud7688092016-11-17 00:08:57 -0500276 input_name = method.get('input_type')
277 input_ref = False
278 if not is_base_type(input_name):
Khen Nursimulubd24f552016-11-18 11:31:15 -0500279 input_name = remove_first_character_if_match(input_name, '.')
280 # input_name = input_name.replace(".", "-")
Khen Nursimulud7688092016-11-17 00:08:57 -0500281 input_name = input_name.split('.')[-1]
Khen Nursimulubd24f552016-11-18 11:31:15 -0500282 referenced_messages.append(input_name)
Khen Nursimulud7688092016-11-17 00:08:57 -0500283 input_ref = True
Khen Nursimulubd24f552016-11-18 11:31:15 -0500284
Khen Nursimulud7688092016-11-17 00:08:57 -0500285 output_name = method.get('output_type')
286 output_ref = False
287 if not is_base_type(output_name):
Khen Nursimulubd24f552016-11-18 11:31:15 -0500288 output_name = remove_first_character_if_match(output_name, '.')
289 # output_name = output_name.replace(".", "-")
Khen Nursimulud7688092016-11-17 00:08:57 -0500290 output_name = output_name.split('.')[-1]
Khen Nursimulubd24f552016-11-18 11:31:15 -0500291 referenced_messages.append(output_name)
Khen Nursimulud7688092016-11-17 00:08:57 -0500292 output_ref = True
Khen Nursimulubd24f552016-11-18 11:31:15 -0500293
Khen Nursimulud7688092016-11-17 00:08:57 -0500294 methods.append(
295 {
296 'method': method.get('name', ''),
297 'input': input_name,
298 'input_ref': input_ref,
299 'output': output_name,
300 'output_ref': output_ref,
Khen Nursimulubd24f552016-11-18 11:31:15 -0500301 'description': remove_unsupported_characters(method.get(
302 '_description', '')),
Khen Nursimulud7688092016-11-17 00:08:57 -0500303 'server_streaming': method.get('server_streaming',
304 False) == True
305 }
306 )
307 services.append(
308 {
309 'service': service.get('name', ''),
310 'methods': methods,
Khen Nursimulubd24f552016-11-18 11:31:15 -0500311 'description': remove_unsupported_characters(service.get(
312 '_description', '')),
Khen Nursimulud7688092016-11-17 00:08:57 -0500313 }
314 )
315 return services
316
317
Khen Nursimulubd24f552016-11-18 11:31:15 -0500318def rchop(thestring, ending):
Khen Nursimulud7688092016-11-17 00:08:57 -0500319 if thestring.endswith(ending):
320 return thestring[:-len(ending)]
321 return thestring
322
323
Khen Nursimulubd24f552016-11-18 11:31:15 -0500324def traverse_desc(descriptor):
325 referenced_messages = []
326 name = rchop(descriptor.get('name', ''), '.proto')
Khen Nursimulud7688092016-11-17 00:08:57 -0500327 package = descriptor.get('package', '')
328 description = descriptor.get('_description', '')
Khen Nursimulubd24f552016-11-18 11:31:15 -0500329 messages = traverse_messages(descriptor.get('message_type', []),
330 package, referenced_messages)
331 enums = traverse_enums(descriptor.get('enum_type', []), package)
332 services = traverse_services(descriptor.get('service', []),
333 referenced_messages)
Khen Nursimulud7688092016-11-17 00:08:57 -0500334 # extensions = _traverse_extensions(descriptors)
335 # options = _traverse_options(descriptors)
Khen Nursimulubd24f552016-11-18 11:31:15 -0500336 set_messages_keys(messages)
337 unique_referred_messages_with_keys = []
338 for message_name in list(set(referenced_messages)):
339 unique_referred_messages_with_keys.append(
340 {
341 'name': message_name,
342 'key': get_message_key(message_name, messages)
343 }
344 )
Khen Nursimulud7688092016-11-17 00:08:57 -0500345
346 data = {
347 'name': name,
348 'package': package,
Khen Nursimulubd24f552016-11-18 11:31:15 -0500349 'description': description,
Khen Nursimulud7688092016-11-17 00:08:57 -0500350 'messages': messages,
351 'enums': enums,
352 'services': services,
Khen Nursimulubd24f552016-11-18 11:31:15 -0500353 'referenced_messages': list(set(referenced_messages)),
354 # TODO: simplify for easier jinja2 template use
355 'referred_messages_with_keys': unique_referred_messages_with_keys,
Khen Nursimulud7688092016-11-17 00:08:57 -0500356 # 'extensions': extensions,
357 # 'options': options
358 }
Khen Nursimulud7688092016-11-17 00:08:57 -0500359 return data
360
361
Khen Nursimulubd24f552016-11-18 11:31:15 -0500362def set_messages_keys(messages):
363 for message in messages:
364 message['key'] = _get_message_key(message)
365 if message['messages']:
366 set_messages_keys(message['messages'])
367
368
369def _get_message_key(message):
370 # assume key is first yang base type field
371 for field in message['fields']:
372 if not field['type_ref']:
373 return field['name']
374 # no key yet - search nested messaged
375 if message['messages']:
376 return get_message_key(message['messages'])
377 else:
378 return None
379
380
381def get_message_key(message_name, messages):
382 for message in messages:
383 if message_name == message['name']:
384 return message['key']
385 if message['messages']:
386 return get_message_key(message_name, message['messages'])
387 return None
388
389
Khen Nursimulud7688092016-11-17 00:08:57 -0500390def generate_code(request, response):
391 assert isinstance(request, plugin.CodeGeneratorRequest)
392
393 parser = DescriptorParser()
394
395 # idx = 1
396 for proto_file in request.proto_file:
397 native_data = parser.parse_file_descriptor(proto_file,
398 type_tag_name='_type',
399 fold_comments=True)
400
401 # print native_data
Khen Nursimulubd24f552016-11-18 11:31:15 -0500402 yang_data = traverse_desc(native_data)
Khen Nursimulud7688092016-11-17 00:08:57 -0500403
404 f = response.file.add()
Khen Nursimulubd24f552016-11-18 11:31:15 -0500405 # TODO: We should have a separate file for each output. There is an
Khen Nursimulud7688092016-11-17 00:08:57 -0500406 # issue reusing the same filename with an incremental suffix. Using
407 # a different file name works but not the actual proto file name
Khen Nursimulu95b919d2016-11-18 16:20:20 -0500408 f.name = '{}-{}'.format('ietf', proto_file.name.replace('.proto',
409 '.yang'))
Khen Nursimulud7688092016-11-17 00:08:57 -0500410 # f.name = '{}_{}{}'.format(_rchop(proto_file.name, '.proto'), idx,
411 # '.yang')
412 # idx += 1
413 f.content = template_yang.render(module=yang_data)
414
Khen Nursimulubd24f552016-11-18 11:31:15 -0500415
Khen Nursimulud7688092016-11-17 00:08:57 -0500416def get_yang_type(field):
417 type = field['type']
418 if type in YANG_TYPE_MAP.keys():
419 _type, _ = YANG_TYPE_MAP[type]
420 if _type in ['enumeration', 'message', 'group']:
421 return field['type_name'].split('.')[-1]
Khen Nursimulubd24f552016-11-18 11:31:15 -0500422 # return remove_first_character_if_match(field['type_name'],
423 # '.').replace('.', '-')
Khen Nursimulud7688092016-11-17 00:08:57 -0500424 else:
425 return _type
426 else:
427 return type
428
Khen Nursimulubd24f552016-11-18 11:31:15 -0500429
Khen Nursimulud7688092016-11-17 00:08:57 -0500430def is_base_type(type):
431 # check numeric value of the type first
432 if type in YANG_TYPE_MAP.keys():
433 _type, _ = YANG_TYPE_MAP[type]
434 return _type not in ['message', 'group']
435 else:
436 # proto name of the type
Khen Nursimulubd24f552016-11-18 11:31:15 -0500437 result = [_format for (_, _format) in YANG_TYPE_MAP.values() if
438 _format == type and _format not in ['message', 'group']]
Khen Nursimulud7688092016-11-17 00:08:57 -0500439 return len(result) > 0
440
Khen Nursimulubd24f552016-11-18 11:31:15 -0500441
Khen Nursimulud7688092016-11-17 00:08:57 -0500442def remove_unsupported_characters(text):
Khen Nursimulubd24f552016-11-18 11:31:15 -0500443 unsupported_characters = ["{", "}", "[", "]", "\"", "\\", "*", "/"]
444 return ''.join([i if i not in unsupported_characters else ' ' for i in
445 text])
446
447
448def remove_first_character_if_match(str, char):
449 if str.startswith(char):
450 return str[1:]
451 return str
452
Khen Nursimulud7688092016-11-17 00:08:57 -0500453
454YANG_TYPE_MAP = {
455 FieldDescriptor.TYPE_BOOL: ('boolean', 'boolean'),
456 FieldDescriptor.TYPE_BYTES: ('binary', 'byte'),
457 FieldDescriptor.TYPE_DOUBLE: ('decimal64', 'double'),
458 FieldDescriptor.TYPE_ENUM: ('enumeration', 'enum'),
459 FieldDescriptor.TYPE_FIXED32: ('int32', 'int64'),
460 FieldDescriptor.TYPE_FIXED64: ('int64', 'uint64'),
461 FieldDescriptor.TYPE_FLOAT: ('decimal64', 'float'),
462 FieldDescriptor.TYPE_INT32: ('int32', 'int32'),
463 FieldDescriptor.TYPE_INT64: ('int64', 'int64'),
464 FieldDescriptor.TYPE_SFIXED32: ('int32', 'int32'),
465 FieldDescriptor.TYPE_SFIXED64: ('int64', 'int64'),
466 FieldDescriptor.TYPE_STRING: ('string', 'string'),
467 FieldDescriptor.TYPE_SINT32: ('int32', 'int32'),
468 FieldDescriptor.TYPE_SINT64: ('int64', 'int64'),
469 FieldDescriptor.TYPE_UINT32: ('uint32', 'int64'),
470 FieldDescriptor.TYPE_UINT64: ('uint64', 'uint64'),
471 FieldDescriptor.TYPE_MESSAGE: ('message', 'message'),
472 FieldDescriptor.TYPE_GROUP: ('group', 'group')
473}
474
475if __name__ == '__main__':
476 # Read request message from stdin
477 data = sys.stdin.read()
478
479 # Parse request
480 request = plugin.CodeGeneratorRequest()
481 request.ParseFromString(data)
482
483 # Create response
484 response = plugin.CodeGeneratorResponse()
485
486 # Generate code
487 generate_code(request, response)
488
489 # Serialise response message
490 output = response.SerializeToString()
491
492 # Write to stdout
493 sys.stdout.write(output)
Khen Nursimulubd24f552016-11-18 11:31:15 -0500494 # print is_base_type(9)