attrs: subclass with mandatory attribute cannot be created when the base class has a factory based one

failing example

import attr

def test_example():

    @attr.s
    class Base(object):
        attr = attr.ib(default=False)

    @attr.s
    class Sub(Base):
        needed = attr.ib()

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 24 (8 by maintainers)

Commits related to this issue

Most upvoted comments

For anyone stumbling on this issue and still unclear on how to implement a keyword-only attribute, here’s the syntax:

@attr.s
class MyClass:
    x = attr.ib(kw_only=True)

Can anyone tell me what is the recommended way now?

I accidentally opened up a duplicate of this.

My personal opinion™ is that we should just allow users to mark attributes as init='kwonly'. That will allow users to fix the problem themselves on Python 3 and consider upgrading on Python 2. 😽

Full example:

@attr.s
class A:
    a = attr.ib()
    b = attr.ib(init='kwonly', default=1)

    # __init__ signature is:
    def __init__(self, a, *, b=1):
        pass

@attr.s
class B(A):
    c = attr.ib()

    # __init__ signature is:
    def __init__(self, a, c, *, b=1):
        pass

Also, in the case of the error we have now, a helpful error message can direct users to use kwonly.

Either make the subclass kw_only=True or overwrite the attribute with the default in the subclass:

import attr

def test_example():

    @attr.s
    class Base(object):
        a = attr.ib(default=False)

    @attr.s
    class Sub(Base):
        a = attr.ib()
        needed = attr.ib()

I think this should be working code:

@attr.s
class Base():
    optional = attr.ib( default = False, kw_only = True )
    _internal = attr.ib( default = None, init = False )
@attr.s
class Subclass( Base ):
    required = attr.ib()

It isn’t though, currently, because:

  • _internal (which is kw_only = False) can not follow optional (which is kw_only = True), even though _internal is init = False and wouldn’t appear in an __init__() arg list anyway. (Is this a separate bug?)

  • required (which is kw_only = False) can not follow optional (which is kw_only = True). While I understand the design here, I think it is an imposition on developers. At the very least it would be nice to be able to opt-in to being able to this with something like:

@attr.s( group_kw_only_last_in_init = True )
class Subclass:
    required = attr.ib()

(Obviously with a better option name than that. 😉)

or like this

@attr.s()
class Main(object):
     required = attr.ib()
     options = attr.embed(SmallOptions)

i see what you mean, in python3 its *, and valid, for python2 there is need for a workaround in form of a sentinel object and exploding then

they can always be given as keyword

the order should stay the same

it should be possible to give them as keyword instead of a positional argument

once inheritance is involved it seems sensible to go towards keywords in any case

basically all mandatory arguments after a optional one can be primary keyword based