I am trying to send a class object from a client to the server side. I've tried to pickle the class and send the pickled bytes through socket but the value isn't pass over to the server side.
client side utils.py
from collections import defaultdict
class Utils:
_filter_ip = []
_filter_vrf = defaultdict(list)
@classmethod
def filter_ip(cls):
return cls._filter_ip
@classmethod
def set_filter_ip(cls, ip_list):
cls._filter_ip = ip_list
@classmethod
def filter_vrf(cls):
return cls._filter_vrf
@classmethod
def set_filter_vrf(cls, device_name, vrf_list):
cls._filter_vrf[device_name] = vrf_list
client side client.py
import socket
import pickle
from utils import Utils
HOST = '127.0.0.1'
PORT = 5001
s = socket.socket()
s.connect((HOST, PORT))
Utils.set_filter_ip(['0.0.0.0/0', '10.0.0.0/8'])
Utils.set_filter_vrf('device1', ['vrf1', 'vrf2'])
pickled_data = pickle.dumps(Utils)
s.send(pickled_data)
server side utils.py
from collections import defaultdict
class Utils:
_filter_ip = []
_filter_vrf = defaultdict(list)
@classmethod
def filter_ip(cls):
return cls._filter_ip
@classmethod
def set_filter_ip(cls, ip_list):
cls._filter_ip = ip_list
@classmethod
def filter_vrf(cls):
return cls._filter_vrf
@classmethod
def set_filter_vrf(cls, device_name, vrf_list):
cls._filter_vrf[device_name] = vrf_list
server side server.py
import socket
import os
import json
import pickle
from utils import Utils
class Server:
_SERVER_HOST = '127.0.0.1'
_SERVER_PORT = 5001
_BUFFER_SIZE = 4096
_SEPARATOR = '<SEPERATOR>'
def __init__(self):
self._socket = socket.socket()
self._client = None
def init_socket(self):
self._socket.bind((self._SERVER_HOST, self._SERVER_PORT))
self._socket.listen(5)
def listen_socket(self):
self._client, address = self._socket.accept()
def recv_data(self):
data = self._client.recv(self._BUFFER_SIZE)
pickled_data = pickle.loads(data)
print(pickled_data.filter_ip())
def run_server(self):
self.init_socket()
self.listen_socket()
while True:
self.recv_data()
the recv_data method prints the unpickled object from Utils class but the data inside the Utils class is missing (both filter_ip and filter_vrf). Would need some help to point out the mistake i made.
CodePudding user response:
It is a good idea to read the documentation. In this section, it is explained that:
Note that functions (built-in and user-defined) are pickled by “fully qualified” name reference, not by value. [2] This means that only the function name is pickled, along with the name of the module the function is defined in. Neither the function’s code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised. [3]
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply.
However, in this section and example of how to allow for the custom pickling of a given class could be implemented:
import io import pickle class MyClass: my_attribute = 1 class MyPickler(pickle.Pickler): def reducer_override(self, obj): """Custom reducer for MyClass.""" if getattr(obj, "__name__", None) == "MyClass": return type, (obj.__name__, obj.__bases__, {'my_attribute': obj.my_attribute}) else: # For any other object, fallback to usual reduction return NotImplemented f = io.BytesIO() p = MyPickler(f) p.dump(MyClass) del MyClass unpickled_class = pickle.loads(f.getvalue()) assert isinstance(unpickled_class, type) assert unpickled_class.__name__ == "MyClass" assert unpickled_class.my_attribute == 1
So in your case, if it is really this simple, something like:
class MyPickler(pickle.Pickler):
def reducer_override(self, obj):
"""Custom reducer for Utils."""
if getattr(obj, "__name__", None) == "Utils":
return type, (obj.__name__, obj.__bases__, vars(obj))
else:
# For any other object, fallback to usual reduction
return NotImplemented
could work.