Search code examples
pythonpython-3.xpython-2.7futurenew-style-class

How to import object from builtins affecting just one class?


I am converting code from python2 to python3 for newstyle classes using future. My project is in Django 1.11

I have a class in forms.py as:

class Address:
    ...rest of code...

class AddressForm(Address, forms.ModelForm):
    ...rest of code...

in Python 2

which is converted to :

from buitlins import object
class Address(object):
        ...rest of code...

class AddressForm(Address, forms.ModelForm):
    ...rest of code...

in Python 3

I have a selenium test that fails when this Form is invoked after it is converted to Python3 with the following error:

File "<path_to_venv>/local/lib/python2.7/site-packages/django/utils/six.py", line 842, in <lambda>
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
File "<path_to_venv>/local/lib/python2.7/site-packages/future/types/newobject.py", line 78, in __unicode__
s = type(self).__str__(self)
RuntimeError: maximum recursion depth exceeded

However, when I remove the import from buitlins import object the test passes.

But as I have added a future check, I get a future difference error & thus every class has to be converted to newstyle. I want it to work in both Python2 and Python3.

Is there a way this module builtins module import can affect just one class and not others in the forms.py file. Or is there some other method to handle this?


Solution

  • The problem you're running up against seems to be from two different Python 2 modernization tools fighting. You seem to be using the python_2_unicode_compatible decorator from django.utils.six

    def python_2_unicode_compatible(klass):
        """
        A decorator that defines __unicode__ and __str__ methods under Python 2.
        Under Python 3 it does nothing.
        To support Python 2 and 3 with a single code base, define a __str__ method
        returning text and apply this decorator to the class.
        """
        if PY2:
            if '__str__' not in klass.__dict__:
                raise ValueError("@python_2_unicode_compatible cannot be applied "
                                 "to %s because it doesn't define __str__()." %
                                 klass.__name__)
            klass.__unicode__ = klass.__str__
            klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
        return klass
    

    and inheriting from newobject, which has this __unicode__ method

    def __unicode__(self):
        # All subclasses of the builtin object should have __str__ defined.
        # Note that old-style classes do not have __str__ defined.
        if hasattr(self, '__str__'):
            s = type(self).__str__(self)
        else:
            s = str(self)
        if isinstance(s, unicode):
            return s
        else:
            return s.decode('utf-8')
    

    And because the two have slightly different strategies for providing both __unicode__ and __str__ methods, they ed up calling each other infinitely, which leads to your recursion error.

    The module that provides builtins.object provides its own python_2_unicode_compatible decorator. Have you tried using that over the one from django.utils.six?