Skip to content

Commit

Permalink
Fix unbound __str__ on Python 2 when slots=True (#199)
Browse files Browse the repository at this point in the history
Previously:

```pycon
>>> import attr
>>> @attr.s(str=True, slots=True)
... class A(object):
...     a = attr.ib()
...
>>> a = A(1)
>>> str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method repr_() must be called with A instance as first argument (got nothing instead)
```

Fixes #198.
  • Loading branch information
rouge8 authored and hynek committed Jun 1, 2017
1 parent f8dab9b commit a6cf2d5
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 9 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Deprecations:
Changes:
^^^^^^^^

*none*
- Fix *str* on Python 2 when ``slots=True``.
`#198 <https://github.com/python-attrs/attrs/issues/198>`_


----
Expand Down
10 changes: 5 additions & 5 deletions src/attr/_make.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,7 @@ def wrap(cls):
# Can't compare function objects because Python 2 is terrible. :(
effectively_frozen = _has_frozen_superclass(cls) or frozen
if repr is True:
cls = _add_repr(cls, ns=repr_ns)
if str is True:
cls.__str__ = cls.__repr__
cls = _add_repr(cls, ns=repr_ns, str=str)
if cmp is True:
cls = _add_cmp(cls)

Expand Down Expand Up @@ -516,9 +514,9 @@ def ge(self, other):
return cls


def _add_repr(cls, ns=None, attrs=None):
def _add_repr(cls, ns=None, attrs=None, str=False):
"""
Add a repr method to *cls*.
Add a repr method to *cls*. If *str* is True, also add __str__.
"""
if attrs is None:
attrs = [a for a in cls.__attrs_attrs__ if a.repr]
Expand All @@ -543,6 +541,8 @@ def repr_(self):
for a in attrs)
)
cls.__repr__ = repr_
if str is True:
cls.__str__ = repr_
return cls


Expand Down
6 changes: 3 additions & 3 deletions tests/test_dunders.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,15 @@ class C(object):

assert "C(_x=42)" == repr(i)

@pytest.mark.parametrize("add_str", [True, False])
def test_str(self, add_str):
@given(add_str=booleans(), slots=booleans())
def test_str(self, add_str, slots):
"""
If str is True, it returns the same as repr.
This only makes sense when subclassing a class with an poor __str__
(like Exceptions).
"""
@attributes(str=add_str)
@attributes(str=add_str, slots=slots)
class Error(Exception):
x = attr()

Expand Down

0 comments on commit a6cf2d5

Please sign in to comment.