Revert "Python API: Add enum and union support."
[vpp.git] / src / vpp-api / python / vpp_papi.py
index 1d92a41..ece0e4e 100644 (file)
@@ -23,6 +23,7 @@ import struct
 import json
 import threading
 import fnmatch
 import json
 import threading
 import fnmatch
+import weakref
 import atexit
 from cffi import FFI
 import cffi
 import atexit
 from cffi import FFI
 import cffi
@@ -55,11 +56,14 @@ void vac_set_error_handler(vac_error_callback_t);
 # Barfs on failure, no need to check success.
 vpp_api = ffi.dlopen('libvppapiclient.so')
 
 # Barfs on failure, no need to check success.
 vpp_api = ffi.dlopen('libvppapiclient.so')
 
-def vpp_atexit(self):
+
+def vpp_atexit(vpp_weakref):
     """Clean up VPP connection on shutdown."""
     """Clean up VPP connection on shutdown."""
-    if self.connected:
-        self.logger.debug('Cleaning up VPP on exit')
-        self.disconnect()
+    vpp_instance = vpp_weakref()
+    if vpp_instance.connected:
+        vpp_instance.logger.debug('Cleaning up VPP on exit')
+        vpp_instance.disconnect()
+
 
 vpp_object = None
 
 
 vpp_object = None
 
@@ -112,7 +116,8 @@ class VPP():
     these messages in a background thread.
     """
     def __init__(self, apifiles=None, testmode=False, async_thread=True,
     these messages in a background thread.
     """
     def __init__(self, apifiles=None, testmode=False, async_thread=True,
-                 logger=logging.getLogger('vpp_papi'), loglevel='debug', read_timeout=0):
+                 logger=None, loglevel=None,
+                 read_timeout=0):
         """Create a VPP API object.
 
         apifiles is a list of files containing API
         """Create a VPP API object.
 
         apifiles is a list of files containing API
@@ -120,11 +125,20 @@ class VPP():
         dynamically created reflecting these APIs.  If not
         provided this will load the API files from VPP's
         default install location.
         dynamically created reflecting these APIs.  If not
         provided this will load the API files from VPP's
         default install location.
+
+        logger, if supplied, is the logging logger object to log to.
+        loglevel, if supplied, is the log level this logger is set
+        to report at (from the loglevels in the logging module).
         """
         global vpp_object
         vpp_object = self
         """
         global vpp_object
         vpp_object = self
+
+        if logger is None:
+            logger = logging.getLogger(__name__)
+            if loglevel is not None:
+                logger.setLevel(loglevel)
+
         self.logger = logger
         self.logger = logger
-        logging.basicConfig(level=getattr(logging, loglevel.upper()))
 
         self.messages = {}
         self.id_names = []
 
         self.messages = {}
         self.id_names = []
@@ -136,11 +150,7 @@ class VPP():
         self.message_queue = queue.Queue()
         self.read_timeout = read_timeout
         self.vpp_api = vpp_api
         self.message_queue = queue.Queue()
         self.read_timeout = read_timeout
         self.vpp_api = vpp_api
-        if async_thread:
-            self.event_thread = threading.Thread(
-                target=self.thread_msg_handler)
-            self.event_thread.daemon = True
-            self.event_thread.start()
+        self.async_thread = async_thread
 
         if not apifiles:
             # Pick up API definitions from default directory
 
         if not apifiles:
             # Pick up API definitions from default directory
@@ -168,14 +178,15 @@ class VPP():
             raise ValueError(1, 'Missing JSON message definitions')
 
         # Make sure we allow VPP to clean up the message rings.
             raise ValueError(1, 'Missing JSON message definitions')
 
         # Make sure we allow VPP to clean up the message rings.
-        atexit.register(vpp_atexit, self)
+        atexit.register(vpp_atexit, weakref.ref(self))
 
         # Register error handler
         vpp_api.vac_set_error_handler(vac_error_handler)
 
         # Support legacy CFFI
         # from_buffer supported from 1.8.0
 
         # Register error handler
         vpp_api.vac_set_error_handler(vac_error_handler)
 
         # Support legacy CFFI
         # from_buffer supported from 1.8.0
-        (major, minor, patch) = [int(s) for s in cffi.__version__.split('.', 3)]
+        (major, minor, patch) = [int(s) for s in
+                                 cffi.__version__.split('.', 3)]
         if major >= 1 and minor >= 8:
             self._write = self._write_new_cffi
         else:
         if major >= 1 and minor >= 8:
             self._write = self._write_new_cffi
         else:
@@ -219,14 +230,14 @@ class VPP():
             localdir = os.path.dirname(os.path.realpath(main.__file__))
         else:
             # use cwd if there is no calling script
             localdir = os.path.dirname(os.path.realpath(main.__file__))
         else:
             # use cwd if there is no calling script
-            localdir = os.cwd()
+            localdir = os.getcwd()
         localdir_s = localdir.split(os.path.sep)
 
         def dmatch(dir):
             """Match dir against right-hand components of the script dir"""
             d = dir.split('/')  # param 'dir' assumes a / separator
         localdir_s = localdir.split(os.path.sep)
 
         def dmatch(dir):
             """Match dir against right-hand components of the script dir"""
             d = dir.split('/')  # param 'dir' assumes a / separator
-            l = len(d)
-            return len(localdir_s) > l and localdir_s[-l:] == d
+            length = len(d)
+            return len(localdir_s) > length and localdir_s[-length:] == d
 
         def sdir(srcdir, variant):
             """Build a path from srcdir to the staged API files of
 
         def sdir(srcdir, variant):
             """Build a path from srcdir to the staged API files of
@@ -255,7 +266,6 @@ class VPP():
         if srcdir:
             # we're in the source tree, try both the debug and release
             # variants.
         if srcdir:
             # we're in the source tree, try both the debug and release
             # variants.
-            x = 'vpp/share/vpp/api'
             dirs.append(sdir(srcdir, '_debug'))
             dirs.append(sdir(srcdir, ''))
 
             dirs.append(sdir(srcdir, '_debug'))
             dirs.append(sdir(srcdir, ''))
 
@@ -332,9 +342,8 @@ class VPP():
                       'i32': 'i',
                       'u64': 'Q',
                       'f64': 'd', }
                       'i32': 'i',
                       'u64': 'Q',
                       'f64': 'd', }
-        pack = None
+
         if t in base_types:
         if t in base_types:
-            pack = base_types[t]
             if not vl:
                 if e > 0 and t == 'u8':
                     # Fixed byte array
             if not vl:
                 if e > 0 and t == 'u8':
                     # Fixed byte array
@@ -395,9 +404,9 @@ class VPP():
 
         for k in kwargs:
             if k not in msgdef['args']:
 
         for k in kwargs:
             if k not in msgdef['args']:
-                raise ValueError(1,'Non existing argument [' + k + ']' + \
-                                 ' used in call to: ' + \
-                                 self.id_names[kwargs['_vl_msg_id']] + '()' )
+                raise ValueError(1, 'Non existing argument [' + k + ']' +
+                                    ' used in call to: ' +
+                                 self.id_names[kwargs['_vl_msg_id']] + '()')
 
         for k, v in vpp_iterator(msgdef['args']):
             off += size
 
         for k, v in vpp_iterator(msgdef['args']):
             off += size
@@ -406,21 +415,28 @@ class VPP():
                     if callable(v[1]):
                         e = kwargs[v[0]] if v[0] in kwargs else v[0]
                         if e != len(kwargs[k]):
                     if callable(v[1]):
                         e = kwargs[v[0]] if v[0] in kwargs else v[0]
                         if e != len(kwargs[k]):
-                            raise (ValueError(1, 'Input list length mismatch: %s (%s != %s)' %  (k, e, len(kwargs[k]))))
+                            raise (ValueError(1,
+                                              'Input list length mismatch: '
+                                              '%s (%s != %s)' %
+                                              (k, e, len(kwargs[k]))))
                         size = 0
                         for i in range(e):
                             size += v[1](self, True, buf, off + size,
                                          kwargs[k][i])
                     else:
                         if v[0] in kwargs:
                         size = 0
                         for i in range(e):
                             size += v[1](self, True, buf, off + size,
                                          kwargs[k][i])
                     else:
                         if v[0] in kwargs:
-                            l = kwargs[v[0]]
-                            if l != len(kwargs[k]):
-                                raise ValueError(1, 'Input list length mismatch: %s (%s != %s)' % (k, l, len(kwargs[k])))
+                            kwargslen = kwargs[v[0]]
+                            if kwargslen != len(kwargs[k]):
+                                raise ValueError(1,
+                                                 'Input list length mismatch:'
+                                                 ' %s (%s != %s)' %
+                                                 (k, kwargslen,
+                                                  len(kwargs[k])))
                         else:
                         else:
-                            l = len(kwargs[k])
+                            kwargslen = len(kwargs[k])
                         if v[1].size == 1:
                         if v[1].size == 1:
-                            buf[off:off + l] = bytearray(kwargs[k])
-                            size = l
+                            buf[off:off + kwargslen] = bytearray(kwargs[k])
+                            size = kwargslen
                         else:
                             size = 0
                             for i in kwargs[k]:
                         else:
                             size = 0
                             for i in kwargs[k]:
@@ -431,7 +447,10 @@ class VPP():
                         size = v(self, True, buf, off, kwargs[k])
                     else:
                         if type(kwargs[k]) is str and v.size < len(kwargs[k]):
                         size = v(self, True, buf, off, kwargs[k])
                     else:
                         if type(kwargs[k]) is str and v.size < len(kwargs[k]):
-                            raise ValueError(1, 'Input list length mismatch: %s (%s < %s)' % (k, v.size, len(kwargs[k])))
+                            raise ValueError(1,
+                                             'Input list length mismatch: '
+                                             '%s (%s < %s)' %
+                                             (k, v.size, len(kwargs[k])))
                         v.pack_into(buf, off, kwargs[k])
                         size = v.size
             else:
                         v.pack_into(buf, off, kwargs[k])
                         size = v.size
             else:
@@ -550,7 +569,8 @@ class VPP():
                 raise ValueError('Variable Length Array must be last: ' + name)
             size, s = self.__struct(*f)
             args[field_name] = s
                 raise ValueError('Variable Length Array must be last: ' + name)
             size, s = self.__struct(*f)
             args[field_name] = s
-            if type(s) == list and type(s[0]) == int and type(s[1]) == struct.Struct:
+            if type(s) == list and type(s[0]) == int and \
+               type(s[1]) == struct.Struct:
                 if s[0] < 0:
                     sizes[field_name] = size
                 else:
                 if s[0] < 0:
                     sizes[field_name] = size
                 else:
@@ -580,10 +600,11 @@ class VPP():
 
     def make_function(self, name, i, msgdef, multipart, async):
         if (async):
 
     def make_function(self, name, i, msgdef, multipart, async):
         if (async):
-            f = lambda **kwargs: (self._call_vpp_async(i, msgdef, **kwargs))
+            def f(**kwargs):
+                return self._call_vpp_async(i, msgdef, **kwargs)
         else:
         else:
-            f = lambda **kwargs: (self._call_vpp(i, msgdef, multipart,
-                                                 **kwargs))
+            def f(**kwargs):
+                return self._call_vpp(i, msgdef, multipart, **kwargs)
         args = self.messages[name]['args']
         argtypes = self.messages[name]['argtypes']
         f.__name__ = str(name)
         args = self.messages[name]['args']
         argtypes = self.messages[name]['argtypes']
         f.__name__ = str(name)
@@ -613,13 +634,6 @@ class VPP():
                 multipart = True if name.find('_dump') > 0 else False
                 f = self.make_function(name, i, msgdef, multipart, async)
                 setattr(self._api, name, FuncWrapper(f))
                 multipart = True if name.find('_dump') > 0 else False
                 f = self.make_function(name, i, msgdef, multipart, async)
                 setattr(self._api, name, FuncWrapper(f))
-
-                # old API stuff starts here - will be removed in 17.07
-                if hasattr(self, name):
-                    raise NameError(
-                        3, "Conflicting name in JSON definition: `%s'" % name)
-                setattr(self, name, f)
-                # old API stuff ends here
             else:
                 self.logger.debug(
                     'No such message type or failed CRC checksum: %s', n)
             else:
                 self.logger.debug(
                     'No such message type or failed CRC checksum: %s', n)
@@ -634,7 +648,7 @@ class VPP():
         """Send a binary-packed message to VPP."""
         if not self.connected:
             raise IOError(1, 'Not connected')
         """Send a binary-packed message to VPP."""
         if not self.connected:
             raise IOError(1, 'Not connected')
-        return vpp_api.vac_write(str(buf), len(buf))
+        return vpp_api.vac_write(bytes(buf), len(buf))
 
     def _read(self):
         if not self.connected:
 
     def _read(self):
         if not self.connected:
@@ -664,6 +678,11 @@ class VPP():
         self.control_ping_index = vpp_api.vac_get_msg_index(
             ('control_ping' + '_' + crc[2:]).encode())
         self.control_ping_msgdef = self.messages['control_ping']
         self.control_ping_index = vpp_api.vac_get_msg_index(
             ('control_ping' + '_' + crc[2:]).encode())
         self.control_ping_msgdef = self.messages['control_ping']
+        if self.async_thread:
+            self.event_thread = threading.Thread(
+                target=self.thread_msg_handler)
+            self.event_thread.daemon = True
+            self.event_thread.start()
         return rv
 
     def connect(self, name, chroot_prefix=None, async=False, rx_qlen=32):
         return rv
 
     def connect(self, name, chroot_prefix=None, async=False, rx_qlen=32):
@@ -695,6 +714,7 @@ class VPP():
         """Detach from VPP."""
         rv = vpp_api.vac_disconnect()
         self.connected = False
         """Detach from VPP."""
         rv = vpp_api.vac_disconnect()
         self.connected = False
+        self.message_queue.put("terminate event thread")
         return rv
 
     def msg_handler_sync(self, msg):
         return rv
 
     def msg_handler_sync(self, msg):
@@ -712,8 +732,6 @@ class VPP():
         if hasattr(r, 'context') and r.context > 0:
             context = r.context
 
         if hasattr(r, 'context') and r.context > 0:
             context = r.context
 
-        msgname = type(r).__name__
-
         if context == 0:
             # No context -> async notification that we feed to the callback
             self.message_queue.put_nowait(r)
         if context == 0:
             # No context -> async notification that we feed to the callback
             self.message_queue.put_nowait(r)
@@ -863,6 +881,8 @@ class VPP():
         """
         while True:
             r = self.message_queue.get()
         """
         while True:
             r = self.message_queue.get()
+            if r == "terminate event thread":
+                break
             msgname = type(r).__name__
             if self.event_callback:
                 self.event_callback(msgname, r)
             msgname = type(r).__name__
             if self.event_callback:
                 self.event_callback(msgname, r)