traits icon indicating copy to clipboard operation
traits copied to clipboard

Trait initializers don't work consistent when copying with deepcopy

Open johannesloibl opened this issue 2 years ago • 4 comments

Don't know if this a bug, but at least i stumbled upon it:

According to documentation using "traits_init", overriding "init" or using dynamic initialization with "_name_default" should all lead to the same behavior. Nevertheless using "traits_init" yields different results when copying objects using this method.

import copy
from traits.api import *

class Bla(HasTraits):
    blubb = Int(0)


class BaseModel(HasTraits):
    bla: Bla = Instance(Bla)


class DunderInitModel(BaseModel):
    def __init__(self, **traits):
        super().__init__(**traits)
        self.bla = Bla()


class TraitInitModel(BaseModel):
    def traits_init(self):
        self.bla = Bla()

if __name__ == '__main__':
    m1 = DunderInitModel()
    m1.bla.blubb = 999
    print(f"Model1.bla.blubb (__init__):    {copy.deepcopy(m1).bla.blubb}")

    m2 = TraitInitModel()
    m2.bla.blubb = 999
    print(f"Model2.bla.blubb (traits_init): {copy.deepcopy(m2).bla.blubb}")

prints out:

Model1.bla.blubb (__init__):    999
Model2.bla.blubb (traits_init): 0

Those two operations should get the same result, shouldn't they?

I can see that inside traits.has_traits.HasTraits.clone_traits, traits_init is executed after copy_traits, which overwrites the copied traits.

Using Win10, traits version 6.3.2, Python 3.8

johannesloibl avatar Feb 01 '22 12:02 johannesloibl

Thanks for the report. I'm a bit confused, though: can you clarify the role of the copy in your example code? Did you intend the print statements in your example to be printing values from m1_copy, m2_copy and m3_copy? (Otherwise, I'm not sure what m1_copy, m2_copy and m3_copy are doing. Or are you reporting that simply the act of copying changes the original, which would definitely be a clear bug?)

mdickinson avatar Feb 01 '22 13:02 mdickinson

Note that for traits_init to work in the way that you intend, you'd need to make an assignment self.blubb = 1 inside the traits_init method; just returning 1 won't do anything useful.

mdickinson avatar Feb 01 '22 14:02 mdickinson

Hey, sorry. When trying to reduce the problem in my application to be simply reproducible, this typo "return 1" in traits_init made the example look like it is showing the bug i have (my app uses assignment to self inside traits_init). In my application it's more complicated since i have nested HasTraits objects and there i definitely see a difference between traits_init and init. Let me check again how to reproduce with a simple example, sorry for the confusion.

johannesloibl avatar Feb 01 '22 14:02 johannesloibl

Ok i got it. Now you should be able to reproduce.

johannesloibl avatar Feb 01 '22 15:02 johannesloibl