Search code examples
pythondjangopython-2to3

TypeError: '<' not supported between instances of pyshipping.Package after running 2to3


Python newbie here, very good chance I am doing a silly mistake here..

After losing a good amount of hair and searching for many hours, I am still not able to convert a whole project to python 3. I have a project made in django framework and it uses python 3.7, and I wanted to incorporate this library into my app. But, because pyshipping uses python 2.7, I thought it may cause compatibility issues. Following this answer,I converted the whole project and tried running this file binpack_simple.py. But it gives me an error I am not able to understand at all. When I run this file using my pycharm terminal when the project iterpreter is set to python 2.7 it runs perfectly, but when I set iterpreter to 3.7, it gives me the following error

return _pyprofile._Utils(Profile).run(statement, filename, sort)
  File "C:\Users\idadarklord\AppData\Local\Programs\Python\Python37\lib\profile.py", line 53, in run
    prof.run(statement)
  File "C:\Users\idadarklord\AppData\Local\Programs\Python\Python37\lib\cProfile.py", line 95, in run
    return self.runctx(cmd, dict, dict)
  File "C:\Users\idadarklord\AppData\Local\Programs\Python\Python37\lib\cProfile.py", line 100, in runctx
    exec(cmd, globals, locals)
  File "<string>", line 1, in <module>
  File "C:/Users/idadarklord/PycharmProjects/untitled/pyshipping/binpack_simple.py", line 230, in test
    bins, rest = binpack(packages)
  File "C:/Users/idadarklord/PycharmProjects/untitled/pyshipping/binpack_simple.py", line 218, in binpack
    return allpermutations(packages, bin, iterlimit)
  File "C:/Users/idadarklord/PycharmProjects/untitled/pyshipping/binpack_simple.py", line 203, in allpermutations
    trypack(bin, todo, bestpack)
  File "C:/Users/idadarklord/PycharmProjects/untitled/pyshipping/binpack_simple.py", line 187, in trypack
    bins, rest = packit(bin, packages)
  File "C:/Users/idadarklord/PycharmProjects/untitled/pyshipping/binpack_simple.py", line 131, in packit
    packages = sorted(originalpackages)
TypeError: '<' not supported between instances of 'Package' and 'Package'

Here is my file. Please let me know if I should upload the whole project for clarifications.

#!/usr/bin/env python
# encoding: utf-8
"""
binpack_simple.py
"""
from builtins import map
from builtins import range
from pyshipping.package import Package

from setuptools import setup, find_packages
from distutils.extension import Extension
import codecs
import time
import random




def packstrip(bin, p):
    """Creates a Strip which fits into bin.

    Returns the Packages to be used in the strip, the dimensions of the strip as a 3-tuple
    and a list of "left over" packages.
    """
    # This code is somewhat optimized and somewhat unreadable
    s = []                # strip
    r = []                # rest
    ss = sw = sl = 0      # stripsize
    bs = bin.heigth       # binsize
    sapp = s.append       # speedup
    rapp = r.append       # speedup
    ppop = p.pop          # speedup
    while p and (ss <= bs):
        n = ppop(0)
        nh, nw, nl = n.size
        if ss + nh <= bs:
            ss += nh
            sapp(n)
            if nw > sw:
                sw = nw
            if nl > sl:
                sl = nl
        else:
            rapp(n)
    return s, (ss, sw, sl), r + p


def packlayer(bin, packages):
    strips = []
    layersize = 0
    layerx = 0
    layery = 0
    binsize = bin.width
    while packages:
        strip, (sizex, stripsize, sizez), rest = packstrip(bin, packages)
        if layersize + stripsize <= binsize:
            packages = rest
            if not strip:
                # we were not able to pack anything
                break
            layersize += stripsize
            layerx = max([sizex, layerx])
            layery = max([sizez, layery])
            strips.extend(strip)
        else:
            # Next Layer please
            packages = strip + rest
            break
    return strips, (layerx, layersize, layery), packages


def packbin(bin, packages):
    packages.sort()
    layers = []
    contentheigth = 0
    contentx = 0
    contenty = 0
    binsize = bin.length
    while packages:
        layer, (sizex, sizey, layersize), rest = packlayer(bin, packages)
        if contentheigth + layersize <= binsize:
            packages = rest
            if not layer:
                # we were not able to pack anything
                break
            contentheigth += layersize
            contentx = max([contentx, sizex])
            contenty = max([contenty, sizey])
            layers.extend(layer)
        else:
            # Next Bin please
            packages = layer + rest
            break
    return layers, (contentx, contenty, contentheigth), packages


def packit(bin, originalpackages):
    packedbins = []
    packages = sorted(originalpackages)
    while packages:
        packagesinbin, (binx, biny, binz), rest = packbin(bin, packages)
        if not packagesinbin:
            # we were not able to pack anything
            break
        packedbins.append(packagesinbin)
        packages = rest
    # we now have a result, try to get a better result by rotating some bins

    return packedbins, rest


# In newer Python versions these van be imported:
# from itertools import permutations
def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = list(map(tuple, args)) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x + [y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)


def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(list(range(n)), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)


class Timeout(Exception):
    pass


def allpermutations_helper(permuted, todo, maxcounter, callback, bin, bestpack, counter):
    if not todo:
        return counter + callback(bin, permuted, bestpack)
    else:
        others = todo[1:]
        thispackage = todo[0]
        for dimensions in set(permutations((thispackage[0], thispackage[1], thispackage[2]))):
            thispackage = Package(dimensions, nosort=True)
            if thispackage in bin:
                counter = allpermutations_helper(permuted + [thispackage], others, maxcounter, callback,
                                                 bin, bestpack, counter)
            if counter > maxcounter:
                raise Timeout('more than %d iterations tries' % counter)
        return counter


def trypack(bin, packages, bestpack):
    bins, rest = packit(bin, packages)
    if len(bins) < bestpack['bincount']:
        bestpack['bincount'] = len(bins)
        bestpack['bins'] = bins
        bestpack['rest'] = rest
    if bestpack['bincount'] < 2:
        raise Timeout('optimal solution found')
    return len(packages)


def allpermutations(todo, bin, iterlimit=5000):
    random.seed(1)
    random.shuffle(todo)
    bestpack = dict(bincount=len(todo) + 1)
    try:
        # First try unpermuted
        trypack(bin, todo, bestpack)
        # now try permutations
        allpermutations_helper([], todo, iterlimit, trypack, bin, bestpack, 0)
    except Timeout:
        pass
    return bestpack['bins'], bestpack['rest']


def binpack(packages, bin=None, iterlimit=5000):
    """Packs a list of Package() objects into a number of equal-sized bins.

    Returns a list of bins listing the packages within the bins and a list of packages which can't be
    packed because they are to big."""
    if not bin:
        bin = Package("600x400x400")
    return allpermutations(packages, bin, iterlimit)


def test():
    fd = open('small.txt')
    vorher = 0
    nachher = 0
    start = time.time()
    for line in fd:
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = binpack(packages)
        if rest:
            print(("invalid data", rest, line))
        else:
            vorher += len(packages)
            nachher += len(bins)
    # print((time.time() - start))
    print((vorher, nachher, float(nachher) / vorher * 100))

#
if __name__ == '__main__':
    import cProfile
    cProfile.run('test()')


# packlayer(bin, packages)

Here is an online link to the file inside the project.


Solution

  • In Python 3 support for the __cmp__ method has been removed. You need to provide a __lt__ method for the class instead if you want to compare two instances. The code for the original Package.__cmp__ is here.

    The new method will probably look like:

    def __lt__(self, other):
        return self.volume < other.volume
    

    but obviously you should test this thoroughly.