attrs
attrs copied to clipboard
"No mandatory attributes after..." error message
I was getting the
No mandatory attributes allowed after an attribute with a default value or factory.
error message related to issues #38 and #93. I had been stumped as to what the order of attributes was for my class and which ones had and which lacked defaults/factory methods. It would be super useful if the exception printed more about the offending attributes. In my case, I wanted to know:
"What is the first attribute that has a default that appears ahead of an attribute which lacks one?"
right now, the error message only tells me what attribute lacks a default.
E.g., I edited _make.py around line 331 (as of version 17.4) to this:
had_default = False
which_had_default = None
for a in attrs:
if had_default is True and a.default is NOTHING and a.init is True:
raise ValueError(
"No mandatory attributes allowed after an attribute with a "
"default value or factory. Attribute in question: {a!r} appears after {before!r}"
.format(a=a, before=which_had_default)
)
elif had_default is False and \
a.default is not NOTHING and \
a.init is not False:
had_default = True
which_had_default = a
(Printing only this message was not quite sufficient for my pretty funky case, and so I ended up printing out the full ordered list of attributes to see which were appearing out of reverse-MRO order and causing my problem. It might be even more helpful to print out the full attrs
list in the even of an error.)
It might also be good to better document / message that kw_only
can be used to address this use case:
# kw default in parent
@attr.s(auto_attribs=True, hash=True, collect_by_mro=True)
class A:
a: int = attr.field(default=1, kw_only=True)
@attr.s(auto_attribs=True, hash=True, collect_by_mro=True)
class B(A):
b: int
B(1)
# kw required in subclass
@attr.s(auto_attribs=True, hash=True, collect_by_mro=True)
class A:
a: int = 1
@attr.s(auto_attribs=True, hash=True, collect_by_mro=True)
class B(A):
b: int = attr.field(kw_only=True)
B(b=1)
Just leaving a note about this - it's a little more explicit now, like @aleaverfay had suggested. My error message (post stack trace):
ValueError: No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: Attribute(name='input', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=typing.Tuple[pathlib.Path, pathlib.Path], converter=None, kw_only=False, inherited=False, on_setattr=None, alias=None)