Skip to content

Implement __init_subclass__ #1357

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

Closed
wants to merge 1 commit into from
Closed

Implement __init_subclass__ #1357

wants to merge 1 commit into from

Conversation

dvtkrlbs
Copy link

@dvtkrlbs dvtkrlbs commented Sep 9, 2019

This pull request implements a portion of the PEP 487 this pep also defines __set_name__ dunder method but it is not implemented in this pr.

__init_subclass__ method allows a superclass to initialize al subclasses it takes the subclass and any number of arguments

@dvtkrlbs dvtkrlbs changed the title Implement __init_sublass__ Implement __init_subclass__ Sep 9, 2019
@youknowone
Copy link
Member

Thanks for your contribution. Adding a test about this feature In tests/snippets will be helpful.

Copy link
Contributor

@isidentical isidentical left a comment

Choose a reason for hiding this comment

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

IMHO doing this calls on builtins.__build_class__ doesn't cover all cases.

(general) batuhan@x200-trisquel:~/cpython$ python
Python 3.9.0a0 (heads/master:b607d99, Jul  4 2019, 11:03:12) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class X:           
...     def __init_subclass__(cls):     
...         print(f"initalizing {cls!r}")
... 
>>> Y = type("Y", (X,), {})
initalizing <class '__main__.Y'>
(general) batuhan@x200-trisquel:~/RustPython$ git checkout dvt/init_subclasses 
(general) batuhan@x200-trisquel:~/RustPython$ cargo run
   Compiling rustpython-vm v0.1.0 (/home/batuhan/RustPython/vm)
   Compiling rustpython v0.1.0 (/home/batuhan/RustPython)
    Finished dev [unoptimized + debuginfo] target(s) in 55.62s
     Running `target/debug/rustpython`
Welcome to the magnificent Rust Python 0.1.0 interpreter 😱 🖖
>>>>> class X:
.....     def __init_subclass__(cls):
.....         print(f"initalizing {cls!r}")
..... 
>>>>> Y = type("Y", (X,), {})
>>>>> 

CPython does this notify operation on type object constructor (__new__). The reason
@dvtkrlbs used __build_class__ instead of __new__ is unlike cpython rustpython's __build_class__ doesn't pass it's keyword arguments (class A(kw1=some_value, ..., metaclass=Z)) to type constructor.

Also there is another thing that might be a problem with cpython compatibility.

    /* Special-case __init_subclass__ and __class_getitem__:
       if they are plain functions, make them classmethods */
    tmp = _PyDict_GetItemIdWithError(dict, &PyId___init_subclass__);
    if (tmp != NULL && PyFunction_Check(tmp)) {
        tmp = PyClassMethod_New(tmp);
        if (tmp == NULL)
            goto error;
        if (_PyDict_SetItemId(dict, &PyId___init_subclass__, tmp) < 0) {
            Py_DECREF(tmp);
            goto error;
        }
        Py_DECREF(tmp);
    }
    else if (tmp == NULL && PyErr_Occurred()) {
        goto error;
    }

CPython;

>>> class X:
...     def __init_subclass__(cls):
...         pass
... 
>>> assert isinstance(X.__dict__["__init_subclass__"], classmethod)
>>> 

RustPython;

>>>>> class X:
.....     def __init_subclass__(cls):
.....         pass
..... 
>>>>> assert isinstance(X.__dict__["__init_subclass__"], classmethod)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

This is a special case like #1230

@dvtkrlbs
Copy link
Author

dvtkrlbs commented Sep 9, 2019

Also probably an error in my side but with this edit the parent class cant alter the subclass even though i pass the class as reference

class X:
    def __init_subclass__(cls):
        cls.initialized = True

class Y:
    pass

assert Y.initialized

this does not work even though it should work

@isidentical
Copy link
Contributor

isidentical commented Sep 9, 2019

this does not work even though it should work

I am not familiar with rust (not even a little bit) so just asking.

            let args: Args = Args::new(vec![class.clone()]);

is this clone() method related? In CPython

    type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
    ...
    if (init_subclass(type, kwds) < 0)
        goto error;    

@dvtkrlbs
Copy link
Author

dvtkrlbs commented Sep 9, 2019

Python objects in rustpython lives in Rc (reference counting) and they customize the behaviour. Clone just increments the counter. (Unlike normal objects). So all the reference counting of the python objects is automatic.

@isidentical
Copy link
Contributor

Python objects in rustpython lives in Rc (reference counting) and they customize the behaviour. Clone just increments the counter. (Unlike normal objects)

oh, i see.

@dvtkrlbs
Copy link
Author

dvtkrlbs commented Sep 9, 2019

Hmm you can access args from type_new function it actually passes them i will move the implementation to there

@windelbouwman
Copy link
Contributor

this does not work even though it should work

This is probably caused because __init_subclass__ is not a classmethod. The initialized attribute should be set on the class X in your example, and a lookup on Y will resort to using the attributes on X.

@windelbouwman
Copy link
Contributor

Could you add the python examples now mentioned as comments into the tests/snippets directory? Then we can run those snippets on cpython and rustpython to test for compatibility.

@dralley
Copy link
Contributor

dralley commented Dec 30, 2019

@dvtkrlbs ?

@dvtkrlbs
Copy link
Author

Oh i completely forgot about this

@dvtkrlbs dvtkrlbs closed this Dec 30, 2019
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.

5 participants