Godot: GDScript: Appending to an array or removing elements doesn't trigger setter

Created on 6 Mar 2018  路  8Comments  路  Source: godotengine/godot

Godot version: 3.0.2

Issue description:
Calling append, insert, erase, push_*, pop_*, sort*, clear, resize or remove on an array with a setter doesn't trigger its setter. Calling clear and erase on dictionaries doesn't trigger it either. It works for dictionaries if you add new entries so it should work for arrays too.

Steps to reproduce:
-Add the scripts to nodes

Array:

extends Node

func _ready():
    var a = A.new()

    a.b.append(3) #Or the other methods

class A:
    var b = [1,2] setget setter

    func setter(value):
        print(value) #Never called

Dictionary:

extends Node

func _ready():
    var a = A.new()

    a.b.orange = 4 #erase() and clear() don't trigger it

class A:
    var b = {} setget setter

    func setter(value):
        print(value) #Prints '(orange:4)'
bug confirmed discussion gdscript

Most helpful comment

Actually the setter getting called for modifying a dictionary seems to be a bug?

A setter should only be called, if you set the variable to a different value / object.

If you call append on an object, you are actually calling the getter to get the object and then work on the object. The getter does not care what you do with the object. Its only job is to give you the object.
The setter is never called since you are not setting the variable to a different object. You are just modifying values on the object which is not the same as setting it.

All 8 comments

Actually the setter getting called for modifying a dictionary seems to be a bug?

A setter should only be called, if you set the variable to a different value / object.

If you call append on an object, you are actually calling the getter to get the object and then work on the object. The getter does not care what you do with the object. Its only job is to give you the object.
The setter is never called since you are not setting the variable to a different object. You are just modifying values on the object which is not the same as setting it.

If there's a need for functions to be called when an array or a dictionary is modified then the container classes should get signals, so you can connect a function to the change.

I agree with ShyRed, calling a setter or getter when methods of a container are called is too inconsistent with the usual meaning of setters and getters.

But what should happen now? Should the setter not be called anymore for dictionary modifications? Is there somebody who depends on this behavior? Should somebody open a new issue for container signals? Should I close the issue?

Would need a Godot dev / team member to get an official statement on this.

My 2 cents:

  1. setter should not be called for dictionary changes anymore as this is not how it is handled in all other languages (to my knowledge)
  2. Fixing the bug might break existing projects, so the fix should go into 3.1 and not into a 3.0.x release.
  3. Container signals is a feature request that also needs some discussion on if and how to implement, I guess. Best is to open a new issue for this?

The fact that the setter is called when doing an "indexed set" on the dictionary is because of how GDScript does indexing internally.
Suppose there is a property called transform of type Transform2D.
Some code does transform.o.x = 6. (o is origin in this case)
But, Transform2D is somewhat immutable, as is Vector2, and if the code just naively sets the x of the o, it won't change anything.
So, it would do something like this:

var local_transform = transform
var local_o = transform.o
local_o.x = 6 # Those are vars, so we can change their properties
local_transform.o = local_o
transform = local_transform

Not sure how this should be fixed, since the compiler does not know anything about whether the value is shared (Dictionary, Array, Object) or unshared (Vector2, etc.) at compile time.

Here are some relevant lines in the code:

Can anyone still reproduce this bug in Godot 3.2.1 or 3.2.2beta4?

Note that GDScript is being rewritten for Godot 4.0.

Should still be valid on 3.2.x.

Confirmed on 3.2.1 and 3.2.2beta4 (and also on master).

Was this page helpful?
0 / 5 - 0 ratings