Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 687
Deesha Avatar asked Aug 29 '18 13:08

Deesha


1 Answers

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?

like image 72
Patrick Haugh Avatar answered Oct 23 '22 20:10

Patrick Haugh