Skip to content

Implement __init_subclass__ - #1357

Closed
dvtkrlbs wants to merge 1 commit into
RustPython:masterfrom
dvtkrlbs:init_subclasses
Closed

dvtkrlbs wants to merge 1 commit into
RustPython:masterfrom
dvtkrlbs:init_subclasses

Conversation

@dvtkrlbs

@dvtkrlbs dvtkrlbs commented Sep 9, 2019 •

Copy link
Copy Markdown

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
Copy Markdown
Member

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

@isidentical isidentical left a comment

Copy link
Copy Markdown
Contributor

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

dvtkrlbs commented Sep 9, 2019

Copy link
Copy Markdown
Author

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

isidentical commented Sep 9, 2019 •

Copy link
Copy Markdown
Contributor

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

dvtkrlbs commented Sep 9, 2019 •

Copy link
Copy Markdown
Author

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
Copy Markdown
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

dvtkrlbs commented Sep 9, 2019

Copy link
Copy Markdown
Author

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

@windelbouwman

Copy link
Copy Markdown
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
Copy Markdown
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

dralley commented Dec 30, 2019

Copy link
Copy Markdown
Contributor

@dvtkrlbs ?

@dvtkrlbs

Copy link
Copy Markdown
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