Home > Blockchain >  Sending pickled class object in socket Python
Sending pickled class object in socket Python

Time:03-23

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.

  • Related