-#!/usr/bin/env python
+#!/usr/bin/env python3
+import inspect
import os
import unittest
-import inspect
-from multiprocessing import Process, Pipe
-from pickle import dumps, PicklingError
from framework import VppTestCase
+from multiprocessing import Process, Pipe
+from pickle import dumps
+import six
+from six import moves
+import sys
+
+if sys.version_info < (3,):
+ from aenum import IntEnum, IntFlag
+else:
+ from enum import IntEnum, IntFlag
class SerializableClassCopy(object):
"""
pass
+ def __repr__(self):
+ return '<SerializableClassCopy dict=%s>' % self.__dict__
+
class RemoteClassAttr(object):
"""
def __getattr__(self, attr):
if attr[0] == '_':
- raise AttributeError
+ if not (attr.startswith('__') and attr.endswith('__')):
+ raise AttributeError('tried to get private attribute: %s ',
+ attr)
self._path.append(attr)
return self
def __setattr__(self, attr, val):
if attr[0] == '_':
- super(RemoteClassAttr, self).__setattr__(attr, val)
- return
+ if not (attr.startswith('__') and attr.endswith('__')):
+ super(RemoteClassAttr, self).__setattr__(attr, val)
+ return
self._path.append(attr)
self._remote._remote_exec(RemoteClass.SETATTR, self.path_to_str(),
- True, value=val)
+ value=val)
def __call__(self, *args, **kwargs):
return self._remote._remote_exec(RemoteClass.CALL, self.path_to_str(),
- True, *args, **kwargs)
+ *args, **kwargs)
class RemoteClass(Process):
self._pipe = Pipe() # pipe for input/output arguments
def __repr__(self):
- return repr(RemoteClassAttr(self, None))
+ return moves.reprlib.repr(RemoteClassAttr(self, None))
def __str__(self):
return str(RemoteClassAttr(self, None))
def __getattr__(self, attr):
if attr[0] == '_' or not self.is_alive():
- if hasattr(super(RemoteClass, self), '__getattr__'):
- return super(RemoteClass, self).__getattr__(attr)
- raise AttributeError
+ if not (attr.startswith('__') and attr.endswith('__')):
+ if hasattr(super(RemoteClass, self), '__getattr__'):
+ return super(RemoteClass, self).__getattr__(attr)
+ raise AttributeError('missing: %s', attr)
return RemoteClassAttr(self, attr)
def __setattr__(self, attr, val):
if attr[0] == '_' or not self.is_alive():
- super(RemoteClass, self).__setattr__(attr, val)
- return
+ if not (attr.startswith('__') and attr.endswith('__')):
+ super(RemoteClass, self).__setattr__(attr, val)
+ return
setattr(RemoteClassAttr(self, None), attr, val)
- def _remote_exec(self, op, path=None, ret=True, *args, **kwargs):
+ def _remote_exec(self, op, path=None, *args, **kwargs):
"""
Execute given operation on a given, possibly nested, member remotely.
"""
mutable_args = list(args)
for i, val in enumerate(mutable_args):
if isinstance(val, RemoteClass) or \
- isinstance(val, RemoteClassAttr):
+ isinstance(val, RemoteClassAttr):
mutable_args[i] = val.get_remote_value()
args = tuple(mutable_args)
- for key, val in kwargs.iteritems():
+ for key, val in six.iteritems(kwargs):
if isinstance(val, RemoteClass) or \
- isinstance(val, RemoteClassAttr):
+ isinstance(val, RemoteClassAttr):
kwargs[key] = val.get_remote_value()
# send request
args = self._make_serializable(args)
kwargs = self._make_serializable(kwargs)
self._pipe[RemoteClass.PIPE_PARENT].send((op, path, args, kwargs))
- if not ret:
- # no return value expected
- return None
timeout = self._timeout
# adjust timeout specifically for the .sleep method
- if path.split('.')[-1] == 'sleep':
+ if path is not None and path.split('.')[-1] == 'sleep':
if args and isinstance(args[0], (long, int)):
timeout += args[0]
elif 'timeout' in kwargs:
def _get_local_repr(self, path):
try:
obj = self._get_local_object(path)
- return repr(obj)
+ return moves.reprlib.repr(obj)
except AttributeError:
return None
"""
if self._serializable(obj):
return obj # already serializable
+
copy = SerializableClassCopy()
+
+ """
+ Dictionaries can hold complex values, so we split keys and values into
+ separate lists and serialize them individually.
+ """
+ if (type(obj) is dict):
+ copy.type = type(obj)
+ copy.k_list = list()
+ copy.v_list = list()
+ for k, v in obj.items():
+ copy.k_list.append(self._make_serializable(k))
+ copy.v_list.append(self._make_serializable(v))
+ return copy
+
# copy at least serializable attributes and properties
for name, member in inspect.getmembers(obj):
- if name[0] == '_': # skip private members
- continue
+ # skip private members and non-writable dunder methods.
+ if name[0] == '_':
+ if name in ['__weakref__']:
+ continue
+ if name in ['__dict__']:
+ continue
+ if not (name.startswith('__') and name.endswith('__')):
+ continue
if callable(member) and not isinstance(member, property):
continue
if not self._serializable(member):
- continue
+ member = self._make_serializable(member)
setattr(copy, name, member)
return copy
if type(obj) is tuple:
rv = tuple(rv)
return rv
+ elif (isinstance(obj, IntEnum) or isinstance(obj, IntFlag)):
+ return obj.value
else:
return self._make_obj_serializable(obj)
def _deserialize_obj(self, obj):
+ if (hasattr(obj, 'type')):
+ if obj.type is dict:
+ _obj = dict()
+ for k, v in zip(obj.k_list, obj.v_list):
+ _obj[self._deserialize(k)] = self._deserialize(v)
+ return _obj
return obj
def _deserialize(self, obj):
def quit_remote(self):
""" Quit remote execution """
- self._remote_exec(RemoteClass.QUIT, None, False)
+ self._remote_exec(RemoteClass.QUIT, None)
def get_remote_value(self):
""" Get value of a remotely held object """
@classmethod
def setUpClass(cls):
- # fork new process before clinet connects to VPP
+ # fork new process before client connects to VPP
cls.remote_test = RemoteClass(RemoteVppTestCase)
# start remote process
def __init__(self):
super(RemoteVppTestCase, self).__init__("emptyTest")
+ # Note: __del__ is a 'Finalizer" not a 'Destructor'.
+ # https://docs.python.org/3/reference/datamodel.html#object.__del__
def __del__(self):
if hasattr(self, "vpp"):
- cls.vpp.poll()
- if cls.vpp.returncode is None:
- cls.vpp.terminate()
- cls.vpp.communicate()
+ self.vpp.poll()
+ if self.vpp.returncode is None:
+ self.vpp.terminate()
+ self.vpp.communicate()
@classmethod
def setUpClass(cls, tempdir):
super(RemoteVppTestCase, cls).setUpClass()
os.environ = orig_env
+ @classmethod
+ def tearDownClass(cls):
+ super(RemoteVppTestCase, cls).tearDownClass()
+
@unittest.skip("Empty test")
def emptyTest(self):
""" Do nothing """