From: Ole Troan Date: Tue, 30 Apr 2019 08:04:36 +0000 (+0200) Subject: API: Add support for "defaults" X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=85465588b18fef9c4712f864f512e00741e2d4f2 API: Add support for "defaults" Add support in the API language for specifying a field default. Add default support in Python binding. define foo { u16 mtu [default = 1500]; }; This is client side only. I.e. if the mtu argument is not passed to the foo function, the client language binding will set it default to 1500. Change-Id: I5df43f3cd87cb300b40ca38e15dcab25b40e424a Signed-off-by: Ole Troan --- diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index c60e43e8ed3..fd87b18792b 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -509,18 +509,31 @@ class VPPAPIParser(object): else: p[0] = p[1] + def p_field_options(self, p): + '''field_options : field_option + | field_options field_option''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = { **p[1], **p[2] } + + def p_field_option(self, p): + '''field_option : ID '=' assignee ',' + | ID '=' assignee + ''' + p[0] = { p[1]: p[3] } + def p_declaration(self, p): '''declaration : type_specifier ID ';' - | type_specifier ID '[' ID '=' assignee ']' ';' ''' - if len(p) == 9: - p[0] = Field(p[1], p[2], {p[4]: p[6]}) + | type_specifier ID '[' field_options ']' ';' ''' + if len(p) == 7: + p[0] = Field(p[1], p[2], p[4]) elif len(p) == 4: p[0] = Field(p[1], p[2]) else: self._parse_error('ERROR') self.fields.append(p[2]) - def p_declaration_array(self, p): '''declaration : type_specifier ID '[' NUM ']' ';' | type_specifier ID '[' ID ']' ';' ''' diff --git a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py index ec7334712ab..74a0a62c021 100755 --- a/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/tests/test_vpp_serializer.py @@ -9,6 +9,7 @@ import logging import sys from ipaddress import * + class TestLimits(unittest.TestCase): def test_limit(self): limited_type = VPPType('limited_type_t', @@ -16,14 +17,27 @@ class TestLimits(unittest.TestCase): unlimited_type = VPPType('limited_type_t', [['string', 'name']]) - - b = limited_type.pack({'name':'foobar'}) + b = limited_type.pack({'name': 'foobar'}) self.assertEqual(len(b), 10) - b = unlimited_type.pack({'name':'foobar'}) + b = unlimited_type.pack({'name': 'foobar'}) self.assertEqual(len(b), 10) with self.assertRaises(VPPSerializerValueError): - b = limited_type.pack({'name':'foobar'*3}) + b = limited_type.pack({'name': 'foobar'*3}) + + +class TestDefaults(unittest.TestCase): + def test_defaults(self): + default_type = VPPType('default_type_t', + [['u16', 'mtu', {'default': 1500, 'limit': 0}]]) + + b = default_type.pack({}) + self.assertEqual(len(b), 2) + + nt, size = default_type.unpack(b) + self.assertEqual(len(b), size) + self.assertEqual(nt.mtu, 1500) + class TestAddType(unittest.TestCase): diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py index d1093318ed0..2d775d558b4 100644 --- a/src/vpp-api/python/vpp_papi/vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py @@ -16,6 +16,10 @@ import struct import collections import sys +import logging +from . import vpp_format +import ipaddress +import socket if sys.version_info <= (3, 4): from aenum import IntEnum @@ -27,11 +31,6 @@ if sys.version_info <= (3, 6): else: from enum import IntFlag -import logging -from . import vpp_format -import ipaddress - -import socket # # Set log-level in application by doing e.g.: @@ -69,7 +68,7 @@ def conversion_unpacker(data, field_type): class BaseTypes(object): - def __init__(self, type, elements=0): + def __init__(self, type, elements=0, options=None): base_types = {'u8': '>B', 'string': '>s', 'u16': '>H', @@ -85,10 +84,18 @@ class BaseTypes(object): else: self.packer = struct.Struct(base_types[type]) self.size = self.packer.size + self.options = options + + def __call__(self, args): + self.options = args + return self def pack(self, data, kwargs=None): if not data: # Default to zero if not specified - data = 0 + if self.options and 'default' in self.options: + data = self.options['default'] + else: + data = 0 return self.packer.pack(data) def unpack(self, data, offset, result=None, ntc=False): @@ -148,6 +155,10 @@ class FixedList_u8(object): self.size = self.packer.size self.field_type = field_type + def __call__(self, args): + self.options = args + return self + def pack(self, data, kwargs=None): """Packs a fixed length bytestring. Left-pads with zeros if input data is too short.""" @@ -183,6 +194,10 @@ class FixedList(object): self.name = name self.field_type = field_type + def __call__(self, args): + self.options = args + return self + def pack(self, list, kwargs): if len(list) != self.num: raise VPPSerializerValueError( @@ -214,6 +229,10 @@ class VLAList(object): self.size = self.packer.size self.length_field = len_field_name + def __call__(self, args): + self.options = args + return self + def pack(self, list, kwargs=None): if not list: return b"" @@ -257,6 +276,10 @@ class VLAList_legacy(): self.packer = types[field_type] self.size = self.packer.size + def __call__(self, args): + self.options = args + return self + def pack(self, list, kwargs=None): if self.packer.size == 1: return bytes(list) @@ -298,6 +321,10 @@ class VPPEnumType(object): self.enum = IntFlag(name, e_hash) types[name] = self + def __call__(self, args): + self.options = args + return self + def __getattr__(self, name): return self.enum[name] @@ -338,6 +365,10 @@ class VPPUnionType(object): types[name] = self self.tuple = collections.namedtuple(name, fields, rename=True) + def __call__(self, args): + self.options = args + return self + # Union of variable length? def pack(self, data, kwargs=None): if not data: @@ -383,6 +414,10 @@ class VPPTypeAlias(object): types[name] = self + def __call__(self, args): + self.options = args + return self + def pack(self, data, kwargs=None): if data and conversion_required(data, self.name): try: @@ -423,14 +458,14 @@ class VPPType(object): raise VPPSerializerValueError( 'Unknown message type {}'.format(f_type)) - l = len(f) + fieldlen = len(f) options = [x for x in f if type(x) is dict] if len(options): self.options = options[0] - l -= 1 + fieldlen -= 1 else: self.options = {} - if l == 3: # list + if fieldlen == 3: # list list_elements = f[2] if list_elements == 0: p = VLAList_legacy(f_name, f_type) @@ -443,15 +478,12 @@ class VPPType(object): p = FixedList(f_name, f_type, list_elements) self.packers.append(p) size += p.size - elif l == 4: # Variable length list + elif fieldlen == 4: # Variable length list length_index = self.fields.index(f[3]) p = VLAList(f_name, f_type, f[3], length_index) self.packers.append(p) else: - if f_type == 'string': - p = types[f_type](self.options) - else: - p = types[f_type] + p = types[f_type](self.options) self.packers.append(p) size += p.size @@ -459,6 +491,10 @@ class VPPType(object): self.tuple = collections.namedtuple(name, self.fields, rename=True) types[name] = self + def __call__(self, args): + self.options = args + return self + def pack(self, data, kwargs=None): if not kwargs: kwargs = data diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py index 76ccf1042f6..de72249ac6b 100644 --- a/src/vpp-api/python/vpp_papi/vpp_stats.py +++ b/src/vpp-api/python/vpp_papi/vpp_stats.py @@ -73,7 +73,8 @@ void stat_segment_disconnect (void); uint32_t *stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm); uint32_t *stat_segment_ls (uint8_t ** pattern); -stat_segment_data_t *stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm); +stat_segment_data_t *stat_segment_dump_r (uint32_t * stats, + stat_client_main_t * sm); stat_segment_data_t *stat_segment_dump (uint32_t * counter_vec); void stat_segment_data_free (stat_segment_data_t * res); @@ -133,7 +134,9 @@ def error_vec_list(api, e): return vec def name_vec_list(api, e): - return [ffi.string(e[i]).decode('utf-8') for i in range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL] + return [ffi.string(e[i]).decode('utf-8') for i in + range(api.stat_segment_vec_len(e)) if e[i] != ffi.NULL] + def stat_entry_to_python(api, e): # Scalar index @@ -222,7 +225,8 @@ class VPPStats(object): if rv == ffi.NULL: raise VPPStatsIOError() - return [ffi.string(self.api.stat_segment_index_to_name_r(rv[i], self.client)).decode('utf-8') + return [ffi.string(self.api.stat_segment_index_to_name_r( + rv[i], self.client)).decode('utf-8') for i in range(self.api.stat_segment_vec_len(rv))] def dump(self, counters): diff --git a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py index 6989e9ac9ba..6e228e46e11 100644 --- a/src/vpp-api/python/vpp_papi/vpp_transport_socket.py +++ b/src/vpp-api/python/vpp_papi/vpp_transport_socket.py @@ -193,23 +193,23 @@ class VppTransport(object): hdr = self.socket.recv(16) if not hdr: return - (_, l, _) = self.header.unpack(hdr) # If at head of message + (_, hdrlen, _) = self.header.unpack(hdr) # If at head of message # Read rest of message - msg = self.socket.recv(l) - if l > len(msg): + msg = self.socket.recv(hdrlen) + if hdrlen > len(msg): nbytes = len(msg) - buf = bytearray(l) + buf = bytearray(hdrlen) view = memoryview(buf) view[:nbytes] = msg view = view[nbytes:] - left = l - nbytes + left = hdrlen - nbytes while left: nbytes = self.socket.recv_into(view, left) view = view[nbytes:] left -= nbytes return buf - if l == len(msg): + if hdrlen == len(msg): return msg raise VppTransportSocketIOError(1, 'Unknown socket read error')