Using `ParamSpecString.null_fold_if_empty` in Python

G’day,

Say I have an object like so:

class ObjectA(GObject.Object):
    def __init__(self):
        super().__init__()
        self._property_a = None

    @GObject.Property(type=str)
    def property_a(self) -> str:
        return self._property_a

    @property_a.setter
    def property_a(self, value: str):
        self._property_a = value

I want property_a to be None if the we set it to an empty string. I see that ParamSpecString has a method null_fold_if_empty. How would I apply that to the above code?

GObject.ParamSpecString.null_fold_if_empty is not a method: it’s a field in the GParamSpecString structure.

GParamSpec is not really a property mechanism: it’s a validation API for GValue; you can use GParamSpec independently of GObject properties, if you want to validate a value with semantics that exist outside of the type system’s limits and defaults. For instance, GType defines int and unsigned int types, but if you want to limit the range of valid values to the [-127, 127] interval, you don’t have a “8 bits signed integer type” (like C99’s int8_t) at your disposal. This is where GParamSpec comes in.

GObject properties are implemented in terms of GParamSpec validation and default initialisation, which means that any value set using the GObject API will be validated against the property definition. If you want to tweak the validation rules of GParamSpecString, you will need access to the property definition, which is something that’s usually easier in C than in Python, because pygobject will paper over some of the low level details.

In theory, you could get the GParamSpec of the property_a property and set the field; but GObject.Object.find_property() will return a GObject.ParamSpec, and pygobject does not allow you to convert it to a GParamSpecString. You probably want to file an issue against pygobject.

In practice, though, null_fold_if_empty is not really what you want; you should perform validation and changes yourself in your setter method.

Thanks for the advice. While I was browsing through the code it did occur to me that this was a bit deep in the bag of tricks and not really necessary.