Search code examples
pythonpython-2.7recursionpickleisinstance

Is a type assigned to pickled objects?


I am using Python 2.7 to parse a multi-layered list of GPS coordinates for plotting. The list is parsed recursively, having taken it from sqlite3, and at some point in the process hits a coordinate set that has been pickled. I'm needing a way of detecting it by type, meaning that when I hit pickled data, I need the program to recognize it, unpickle it and continue recursing on it.

Does pickled data have a type or class? So far, all I get are tuples, strings or unicode. I can't assume the data is at a certain depth in the list as sometimes there are multiple coordinate sets in one data stream. I simply want to use isinstance to detect it.

My feeble attempt at solving this is below. (One problem - I cannot upgrade Python or use outside libraries due to this being a work computer.)

def plotCoordinates(self, coordinates, lineColor, lineWidth, tags):
    if coordinates is None:
        return

    elif isinstance(coordinates, list):
        if isinstance(coordinates[0], tuple):   # COORDINATE TUPLE INSIDE THIS LIST?
            # FOUND COORDINATES LIST, PLOT IT
            for inx, coordinate in enumerate(coordinates):
                longitude, latitude = coordinate

                if inx == 0:
                    oldLatitude, oldLongitude = latitude, longitude
                else:
                    self.plotLine(oldLatitude, oldLongitude, latitude, longitude, lineColor, lineWidth, tags, 1)
                    oldLatitude, oldLongitude = latitude, longitude

            return

        else:    # JUST AN ORDINARY LIST, RECURSE ON IT
            for item in coordinates:
                self.plotCoordinates(item, lineColor, lineWidth, tags)
            return

    elif isinstance(coordinates, tuple):
        # DIVE DOWN DEEPER INTO THE LIST
        for item in coordinates:
            self.plotCoordinates(item, lineColor, lineWidth, tags)
        return

    # FOUND PICKLED LIST. EXTRACT IT
    elif isinstance(coordinates, str) or isinstance(coordinates, unicode):
        self.plotCoordinates(pickle.loads(coordinates), lineColor, lineWidth, tags)
        return

    else:
        # ERROR, UNKNOWN TYPE
        print coordinates
        print 'TYPE UNDETECTED: ' + str(type(coordinates))
        return

Solution

  • In Python 2.7, type(pickled_object) will always be str. However, not every str is a pickled object. isinstance("Hello", str) == True, for example, but "Hello" is not a pickled object. You will get a cryptic error if you do pickle.loads("Hello"). There is no specific type for pickled objects that you can check for using isinstance. A pickled object is simply a string that pickle can convert to an object using loads, because pickle originally created it from an object with dumps.

    The most pythonic approach is to try to unpickle the object, and then let the pickle module raise an exception if it cannot do it (as martineau referred to in the comments). The pickle module is the one that creates all pickled objects, and it can best decide whether a string is a pickled object, by trying to get the original object back. If it succeeds, you have a nice unpickled object to plot. If it fails, then all you have is a string that you don't know what to do with, because it isn't a pickled object.

    To implement this, after checking the tuple case, in another elif clause, do:

        elif isinstance(coordinates, str):
            # Strings might be pickled objects, let's see if this one is
            try:
                # Try to unpickle coordinates
                obj = pickle.loads(coordinates)
            except Exception:
                # Coordinates failed to unpickle
                # We cannot plot any coordinates with this input, then
                print coordinates
                print 'String could not be unpickled')
                return
            else:
                # Unpickling created an object, now let's try to plot it
                self.plotCoordinates(obj, lineColor, lineWidth, tags)
                return
        else:
            # ERROR, UNKNOWN TYPE
            print coordinates
            print 'TYPE UNDETECTED: ' + str(type(coordinates))
            return