You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
attrs-decorated classes don't play nicely with sibling __init__ methods #58
attrs-decorated classes can produce unexpected behaviour when they exist in an inheritance tree with non-attrs-decorated classes that define their own __init__ methods, because those __init__ methods aren't called. This dumb test exercises this:
The second assertion currently fails with an AttributeError.
In ordinary Python code this would be addressed with a super call in A.__init__, but I'm not sure if adding a super to the __init__ returned from _attrs_to_script would achieve the same result without breaking a bunch of other cases.
(I apologise for not digging further & submitting a patch; the code in _make.py is a little too terrifying to get stuck into a Monday morning!)
This way we could pass arguments to the super().__init__() method and safely call it using named parameters.
What do you think about it ? Are there any case which would not be covered by this behavior ?
EDIT : Here is a simple implementation which works fine for my case :
# the method is called by passing the cl.__base__ as the base parameterdef_attrs_to_script(attrs, base):
""" Return a valid Python script of an initializer for *attrs*. """# current code generating args does not change# [...]# I append the following :ifbaseisnotobject:
args.append("**kwargs")
supercall=""" frommodule = __import__('{module}', fromlist=[' ']) supclass = getattr(frommodule, '{base}') super(supclass, self).__init__(**kwargs)""".format(base=base.__name__,
module=base.__module__)
else:
supercall=""return"""\def __init__(self, {args}):{supercall} {setters}""".format(
args=", ".join(args),
supercall=supercall,
setters="\n ".join(lines) iflineselse"pass",
)
This works fine with a class such as the following :
Honestly this looks like something with many edge cases and I explicitly do not want to have **kwargs in my __init__s because attrs is all about explicit APIs and zero-cost code generation. This approach adds quite a bit of runtime complexity whenever someone uses subclassing and muddles up the signatures of initializers..
I feel like this might be a great case for writing an attrs extension like @super_init that will implement exactly what you’re describing. attrs was always conceptualized for extensibility because of cases like this.
Another alternative might be once the aforementioned post-init-hook lands (hopefully 16.1) to add an explicit allow_kwargs option that lets you do your thing in __post_init__.
No problem, it was mostly a proof-of-concept to see what you would think about it. I understand your reasoning.
I'll try to realize an extension based on this concept. I'll need to modify the generated __init__ method. Is there any recommended way to do it ?
Not yet! If someone gives me a strong case + sane API I won’t regret in the future, I may make some innards public. But at this point you can only use the internal functions and play around with strings. And those can change quite radically, as seen in #60.
That’s why I suspect a hook-based system is better for everyone involved.
Activity
hynek commentedon Aug 15, 2016
The problem is, that attrs has no way to know, how to call super (i.e. with what arguments).
I believe this could be solved by the suggested solutions to #25 (IOW: support for a custom
__init__hook)?Until then you'll have to write your own
__init__sadly.Insoleet commentedon Aug 16, 2016
I have a suggestion : use something like PyQt5 does ( http://pyqt.sourceforge.net/Docs/PyQt5/multiinheritance.html )
For example, the following code :
would generate something like this :
This way we could pass arguments to the
super().__init__()method and safely call it using named parameters.What do you think about it ? Are there any case which would not be covered by this behavior ?
EDIT : Here is a simple implementation which works fine for my case :
This works fine with a class such as the following :
Calling the constructor like this :
foo = Foo(1, 2, 3, parent=None)QObject.super(parent)is correctly called with None as a value.I needed to import QObject in the generated script code because if I don't, I would get a
NameError : name QObject not found. The emptyfromlistis because of the following fact : http://stackoverflow.com/questions/2724260/why-does-pythons-import-require-fromlisthynek commentedon Aug 17, 2016
Honestly this looks like something with many edge cases and I explicitly do not want to have
**kwargsin my__init__s because attrs is all about explicit APIs and zero-cost code generation. This approach adds quite a bit of runtime complexity whenever someone uses subclassing and muddles up the signatures of initializers..I feel like this might be a great case for writing an attrs extension like
@super_initthat will implement exactly what you’re describing.attrswas always conceptualized for extensibility because of cases like this.Another alternative might be once the aforementioned post-init-hook lands (hopefully 16.1) to add an explicit
allow_kwargsoption that lets you do your thing in__post_init__.Insoleet commentedon Aug 17, 2016
No problem, it was mostly a proof-of-concept to see what you would think about it. I understand your reasoning.
I'll try to realize an extension based on this concept. I'll need to modify the generated
__init__method. Is there any recommended way to do it ?hynek commentedon Aug 17, 2016
Not yet! If someone gives me a strong case + sane API I won’t regret in the future, I may make some innards public. But at this point you can only use the internal functions and play around with strings. And those can change quite radically, as seen in #60.
That’s why I suspect a hook-based system is better for everyone involved.
hynek commentedon Feb 4, 2017
I believe this is fixed by
__attrs_post_init__! Closing for now but please let me know if there’s anything broken.