Runtime Warning when setting an GObject property on Gobject derived instance in python

117 views Asked by At

In my new library I have I like to present stimuli to a user for psychological experiments. Such as presenting circles and other types of visual stimuli or sounds. I like to base my C library on glib and gobject, as that makes it relatively easy to have language bindings, and glib provides many handy algorithms/data structures for my library, so I don't have to write those myself. Many of my intended clients (researchers in the field of psychology/linguistics) might not be proficient in C, but might be relatively handy with a language such as python. So I'm using C to be able to meet the requirements of a psychological experimentation toolkit. I would like to know with millisecond precision when a stimulus is presented to the user. I think for this C is handy, but a friendly API for psychologist and for this Python/javascript API is handy. GObject aims to create bindings to these GObject based C libraries via GObject introspection and generally I'm really happy how this process works out.

I'm having some problems when I try to set a GObject property in python when python does not have a reference to a variable, or perhaps only one in the return tuple. If i'm using setter methods, the code runs just fine. So in the python fragment below, I'm experiencing problem when I do an assignment like this:

instance.prop.property_name = Psy.SomeNewObject()

Setting the property like this:

instance.set_property_name(Psy.SomeNewObject())

or

my_property_value = Psy.SomeNewObject()
instance.prop.property_name = my_property_value

Works like intended and I don't get any errors/warnings. So a small script below is something that I have in my test directory to see whether the bindings work nicely in python. In the example below I'm creating a Canvas (could be a window, or an offscreen egl managed canvas). I'm creating a Circle object which is drawn on the canvas. Circle is derived from an Abstract class PsyVisualStimulus. Each instance of PsyVisualStimulus has a color property, the color in which we would like to draw the VisualStimulus. The Color, Canvas and VisualStimulus all derive from GObject.

#!/usr/bin/env python

import gi
import tempfile

# All object in Psy.* derive from GObject and not GObject.InitiallyUnowned.
# Hence, they cannot have a floating reference. All objects/functions (should) use
# transfer annotations, to determine object lifetime, as is currently advised.

gi.require_version("Psy", "0.1")
from gi.repository import Psy

bg_color = Psy.Color(r=0.5, g=0.5, b=0.5)
fg_color = Psy.Color(r=1.0, g=0.0, b=0.0)

x, y = 0.0, 0.0
radius = 150.0
num_vertices = 100

canvas = Psy.GlCanvas.new(640, 480)
circle = Psy.Circle.new_full(canvas, x, y, num_vertices, radius)
canvas.set_background_color(bg_color)  # this seems fine
circle.props.color = fg_color  # this is fine as we have an ref in python
# The next line prints a warning in the shell, and I would like to fix this!!
circle.props.color = Psy.Color(r=0.5, g=0.5, b=0.5)
circle.props.color = fg_color
circle.play_for(
    canvas.get_time().add(Psy.Duration.new_ms(16)), Psy.Duration.new_ms(50)
)  # Draw circle for 50 ms.
canvas.iterate()
image = canvas.get_image()
image.save_path(tempfile.gettempdir() + "/red-circle.png", "png")

the line circle.props.color = Psy.Color(r=0.5, g=0.5, b=0.5) emits a runtime warning printed to the shell:

sys:1: RuntimeWarning: Expecting to marshal a borrowed reference for <Psy.Color object at 0x7f9e0ee26a80 (PsyColor at 0x5616651dba00)>, but nothing in Python is holding a reference to this object. See: https://bugzilla.gnome.org/show_bug.cgi?id=687522

The line below works precisely as intended and doesn't produce any warnings. So I must be doing something incorrectly in my C library and I would like to have it fixed. Following the link in the warning does not bring me to something I understand and it seems quite old and perhaps outdated. I'll demonstrate fragments of my C code that that the python code above runs through in order to set the color property of a PsyVisualStimulus The Circle in the fragment above derives from PsyVisualStimulus, as PsyVisualStimulus is abstract I cannot instantiate those directly.


// header

#define PSY_TYPE_VISUAL_STIMULUS psy_visual_stimulus_get_type()
G_DECLARE_DERIVABLE_TYPE(
    PsyVisualStimulus, psy_visual_stimulus, PSY, VISUAL_STIMULUS, PsyStimulus)


G_MODULE_EXPORT PsyColor *
psy_visual_stimulus_get_color(PsyVisualStimulus *self);
G_MODULE_EXPORT void
psy_visual_stimulus_set_color(PsyVisualStimulus *self, PsyColor *color);

// relevant parts of the implementation.

G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PsyVisualStimulus,
                                    psy_visual_stimulus,
                                    PSY_TYPE_STIMULUS)


static void
psy_visual_stimulus_set_property(GObject      *object,
                                 guint         property_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
{
    PsyVisualStimulus *self = PSY_VISUAL_STIMULUS(object);

    switch ((VisualStimulusProperty) property_id) {
    // many other properties are omitted from this fragment
    case PROP_COLOR:
        psy_visual_stimulus_set_color(self, g_value_get_object(value));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
    }
}

static void
psy_visual_stimulus_class_init(PsyVisualStimulusClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);
    object_class->get_property = psy_visual_stimulus_get_property;
    object_class->set_property = psy_visual_stimulus_set_property;
    object_class->dispose      = psy_visual_stimulus_dispose;

    PsyStimulusClass *stimulus_class = PSY_STIMULUS_CLASS(klass);
    stimulus_class->play             = visual_stimulus_play;
    stimulus_class->set_duration     = visual_stimulus_set_duration;

    klass->update = visual_stimulus_update;
    
    // The other properties are omitted.

    /**
     * PsyVisualStimulus:color
     *
     * The color `PsyColor` used to fill this object with
     */
    visual_stimulus_properties[PROP_COLOR]
        = g_param_spec_object("color",
                              "Color",
                              "The fill color for this stimulus",
                              PSY_TYPE_COLOR,
                              G_PARAM_READWRITE);

    g_object_class_install_properties(
        object_class, NUM_PROPERTIES, visual_stimulus_properties);
}

/**
 * psy_visual_stimulus_get_color:
 * @self: An instance of `PsyVisualStimulus`
 *
 * Get the color of the stimulus.
 *
 * Returns:(transfer none): the `PsyColor` of used to fill the stimuli
 */
PsyColor *
psy_visual_stimulus_get_color(PsyVisualStimulus *self)
{
    PsyVisualStimulusPrivate *priv
        = psy_visual_stimulus_get_instance_private(self);
    g_return_val_if_fail(PSY_IS_VISUAL_STIMULUS(self), NULL);

    return priv->color;
}

/**
 * psy_visual_stimulus_set_color:
 * @self: an instance of `PsyVisualStimulus`
 * @color:(transfer none): An instance of `PsyVisualStimulus` that is going to
 *         be used in order to fill the shape of the stimulus
 *
 * Set the fill color of the stimulus, this color is used to fill the stimulus
 */
void
psy_visual_stimulus_set_color(PsyVisualStimulus *self, PsyColor *color)
{
    PsyVisualStimulusPrivate *priv
        = psy_visual_stimulus_get_instance_private(self);

    g_return_if_fail(PSY_IS_VISUAL_STIMULUS(self) && PSY_IS_COLOR(color));

    g_clear_object(&priv->color);
    // psy_color_dup creates a deep copy, it calls g_object_new(PSY_TYPE_CIRCLE, NULL) and 
    // It does not copy by increasing the ref count. Perhaps this is wrong.
    // PsyColor Might be converted to a boxed type as is is quite shallow anyway.
    // but that is another issue.
    priv->color = psy_color_dup(color);
}

The warning seems to come from: https://gitlab.gnome.org/GNOME/pygobject/-/blob/3.16.1/gi/pygi-object.c line 111

I would really like to fix this warning, so do I perhaps have errors in the implementation of my GObject derived class? Is there some issue with the GObject annotations? I would really appreciate any help I can get.

0

There are 0 answers