Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't terminate the super() chain in SubclassWithMeta.__init_subclass__ #1234

Closed
wants to merge 2 commits into from
Closed

Conversation

ruohola
Copy link

@ruohola ruohola commented Jul 21, 2020

Resolves #1233

@jkimbo
Copy link
Member

jkimbo commented Jul 23, 2020

@syrusakbary any insight into why super is not being called here?

@syrusakbary
Copy link
Member

Can you add a test @ruohola? It will make a bit easier what you are trying to fix, and it's side effects.

Thanks!

@ruohola
Copy link
Author

ruohola commented Jul 24, 2020

@syrusakbary Added some tests. They are pretty contrived, but I think they should anyways be useful.

Comment on lines +4 to +18
def test_init_subclass_calls_super():
class DefinesInitSubclass:
subclass_init_count = 0

def __init_subclass__(cls, **kwargs):
DefinesInitSubclass.subclass_init_count += 1
super().__init_subclass__(**kwargs)

class InheritsAsFirst(SubclassWithMeta, DefinesInitSubclass):
pass

class InheritsAsLast(DefinesInitSubclass, SubclassWithMeta):
pass

assert DefinesInitSubclass.subclass_init_count == 2
Copy link
Author

@ruohola ruohola Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the reason for this PR is that this example would fail with assert 1 == 2, without the added super() call here:

super().__init_subclass__()

DefinesInitSubclass.passed_kwargs.append(kwargs)
super().__init_subclass__(**kwargs)

class InheritsAsFirst(SubclassWithMeta, DefinesInitSubclass, is_consumed="foo"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not clear to me what happens with the is_consumed="foo" meta variable.

I'm a bit concerned about the inconsistency between InheritsAsFirst and InheritsAsLast

Copy link
Author

@ruohola ruohola Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that inconsistency is there even without this PR. I agree that it's a bit messy, maybe we could do:

try:
    super().__init_subclass__(**meta_options)
except TypeError:
    super().__init_subclass__()

Without the try-except just passing all the kwargs forward with super().__init_subclass__(**meta_options)
will fail when super() is object or any other class with a __init_subclass__ that doesn't take any arguments.

Copy link
Author

@ruohola ruohola Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try:
    super().__init_subclass__(**meta_options)
except TypeError:
    super().__init_subclass__()

Okey, this is actually a bad way to do it, and can hide accidentally suppress unwanted errors. I think that it's fine that the __init_subclass__ consumes all of the kwargs, since it really cannot know if the next class in the MRO is able to receive them.

IMO this PR still removes bunch of confusion by making #1234 (comment) work, and also by documenting the behavior a bit better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need more time to think about this and come to a conclusion. Thanks for the analysis @ruohola

Copy link
Author

@ruohola ruohola Jul 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I truly think this is how the method should behave. The Python docs tell us that we are supposed to take the away needed kwargs and then continue the super chain. Since SubclassWithMeta is specifically made so that it takes use of all of the passed kwargs, we should take all of them away and just continue with the bare super().__init_subclass__().

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@syrusakbary @jkimbo So, what do you think?

@ruohola ruohola closed this Oct 4, 2020
@ruohola ruohola deleted the init-subclass branch October 4, 2020 16:51
@ruohola ruohola restored the init-subclass branch October 4, 2020 16:52
@ruohola ruohola reopened this Oct 4, 2020
@ruohola ruohola closed this Oct 4, 2020
@ruohola ruohola deleted the init-subclass branch October 4, 2020 17:11
@ruohola ruohola restored the init-subclass branch October 4, 2020 17:13
@ruohola ruohola reopened this Oct 4, 2020
@ruohola
Copy link
Author

ruohola commented Oct 4, 2020

Sorry, my git scripts messed up ^^

@ruohola ruohola changed the title Don't terminate the super() chain in SubclassWithMeta.__init_subclass__ Don't terminate the super() chain in SubclassWithMeta.__init_subclass__ Jan 20, 2022
@ruohola
Copy link
Author

ruohola commented Mar 27, 2022

w/e 🤷‍♂️

@ruohola ruohola closed this Mar 27, 2022
@ruohola ruohola deleted the init-subclass branch March 27, 2022 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Why SubclassWithMeta.__init_subclass__ terminates the super() chain?
3 participants