Opened 8 years ago

Closed 8 years ago

Last modified 8 years ago

#26549 closed Bug (wontfix)

HttpResponse.__str__ is missing on Python 3

Reported by: Robert Rollins Owned by: nobody
Component: HTTP handling Version: 1.9
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The HttpResponse class includes this block:

    if six.PY3:
        __bytes__ = serialize
    else:
        __str__ = serialize

Because __str__ is not defined when six.PY3 is True, calling str(response_object) falls back to __repr__ on Python 3. However, __str__ is defined on Python 2, and it returns the full HTTP message.

It seems like bad idea to have the output of the "convert to string" action be different based on Python version, as it makes multi-version code difficult to implement and debug.

Change History (11)

comment:1 by Tim Graham, 8 years ago

What do you think the correct resolution is? The commit where that branch was introduced is e04230e2e406dcf74eeec8d3c95c84362c7da780.

comment:2 by Robert Rollins, 8 years ago

I would remove the else, so that __str__ is defined the same way for both Python2 and Python 3. Heck, the if isn't even needed, since there's no harm in defining __bytes__ on Python 2, as it won't do anything. So I'd ultimately replace the entire block with

    __bytes__ = serialize
    __str__ = serialize

comment:3 by Claude Paroz, 8 years ago

Having a __str__ method on Python 3 which returns bytes might be problematic.
From the Python 3 docs: The return value must be a string object.

comment:4 by Robert Rollins, 8 years ago

Ah, *that's* why the if check is there. My inexperience with Python 3 is showing...

Would it be possible to convert the content data in the response to unicode for __str__? I'm not particularly familiar with Python 3, so I'm not sure if this would be particularly easy.

comment:5 by Aymeric Augustin, 8 years ago

Yes, you just need:

    def __str__(self):
        return bytes(self).decode(self.charset)

I don't think this is a good idea, though, because:

  • you aren't supposed to do anything with this value other than show it to a human
  • full HTTP responses aren't particularly human friendly

What's the use case here?

comment:6 by Robert Rollins, 8 years ago

The place I'm using this is in some tests where I need to check the HTML content of the response. self.assertContains() isn't sufficient, because that cares about the whitespace, and the templates render a lot of extraneous whitespace. So I use self.assertInHTML() instead. Unfortunately, assertInHTML() doesn't accept an HttpResponse object, so I have to send it a string.

Is there a better way to do this with built-in django testing tools?

comment:7 by Aymeric Augustin, 8 years ago

I see. I guess I'd just use content = response.content.decode(response.charset).

comment:8 by Robert Rollins, 8 years ago

I ended up going with content = str(response.content), as that was the only way I found to make the test code compatible with both Python 2 and 3. I think your way probably works for both too, but I'm not really sure. The decode() and encode() functions have been the bane of my existence for years, because I never get them right.

comment:9 by Tim Graham, 8 years ago

Resolution: wontfix
Status: newclosed

comment:10 by Aymeric Augustin, 8 years ago

The decode() and encode() functions have been the bane of my existence for years, because I never get them right.

That's the reason why Python 3 exists... Stop caring about Python 2 now ;-)

comment:11 by Robert Rollins, 8 years ago

If only it were that easy...

Note: See TracTickets for help on using tickets.
Back to Top