How to get paths for relocatable schemas in Gio.Settings?

1.5k Views Asked by At

In Gio.Settings I can list relocatable schemas using

Gio.Settings.list_relocatable_schemas()

and I can use

Gio.Settings.new_with_path(schema_id, path)

to get a Gio.Settings instance. But how can I get all value for path that are currently used for a given schema_id?

3

There are 3 best solutions below

0
On

You can't, at least not for an arbitrary schema, and this is by definition of what a relocatable schema is: a schema that can have multiple instances, stored in multiple arbitrary paths.

Since a relocatable schema instance can be stored basically anywhere inside DConf, gsettings has no way to list their paths, it does not keep track of instances. And dconf can't help you either, as it has no notion of schemas at all, it only knows about paths and keys. It can list the subpaths of a given path, but that's about it.

It's up for the application, when creating multiple instances of a given relocatable schema, to store each instance in a sensible, easily discoverable path, such as a subpath of the (non-relocatable) application schema. Or store the instance paths (or suffixes) as a list key in such schema.

Or both, like Gnome Terminal does with its profiles:

  • org.gnome.Terminal.ProfilesList is a non-relocatable, regular schema, stored at DConf path /org/gnome/terminal/legacy/profiles:/
  • That schema has 2 keys, a default string with a single UUID, and a list list of strings containing UUIDs.
  • Each profile is an instance of the relocatable schema org.gnome.Terminal.Legacy.Profile, and stored at, you guess... /org/gnome/terminal/legacy/profiles:/:<UUID>/!

This way a client can access all instances using either gsettings, reading list and building the paths from the UUIDs, or from dconf, by directly listing the subpaths of /org/gnome/terminal/legacy/profiles:/.

And, of course, for non-relocatable schemas you can always get their paths with:

gsettings list-schemas --print-paths
0
On

Normally, a schema has as fixed path that determines where the settings are stored in the conceptual global tree of settings. However, schemas can also be ‘relocatable’, i.e. not equipped with a fixed path. This is useful e.g. when the schema describes an ‘account’, and you want to be able to store a arbitrary number of accounts.

Isn't the new_with_path just for that? You have to store the schemas somewhere associated with accounts, but that is not the responsibility of the Settings system. I think new_with_path is for the case where your schemas depend on accounts.

I think you can find more information with GSettingsSchemas - this is an example in the Description for a case where the Schema is part of a plugin.

0
On

Unfortunately you cannot do it from Gio.Settings.

I see two options here:

  • Keep separate gsetting to store paths of relocatable schemas
  • Utilize dconf API, which is a low-level configuration system. Since there is no Python binding (guessing it's Python question) I suggest using ctypes for binding with C. If you know the root path of your relocatable schemas you can use below snippet list them.

    import ctypes
    from ctypes import Structure, POINTER, byref, c_char_p,  c_int, util
    
    from typing import List
    
    
    class DconfClient:
        def __init__(self):
            self.__dconf_client = _DCONF_LIB.dconf_client_new()
    
        def list(self, directory: str) -> List[str]:
            length_c = c_int()
            directory_p = c_char_p(directory.encode())
            result_list_c = _DCONF_LIB.dconf_client_list(self.__dconf_client, directory_p, byref(length_c))
    
            result_list = self.__decode_list(result_list_c, length_c.value)
            return result_list
    
        def __decode_list(self, list_to_decode_c, length):
            new_list = []
            for i in range(length):
                # convert to str and remove slash at the end
                decoded_str = list_to_decode_c[i].decode().rstrip("/")
                new_list.append(decoded_str)
            return new_list
    
    
    class _DConfClient(Structure):
        _fields_ = []
    
    
    _DCONF_LIB = ctypes.CDLL(util.find_library("dconf"))
    _DCONF_LIB.dconf_client_new.argtypes = []
    _DCONF_LIB.dconf_client_new.restype = POINTER(_DConfClient)
    _DCONF_LIB.dconf_client_new.argtypes = []
    _DCONF_LIB.dconf_client_list.argtypes = [POINTER(_DConfClient), c_char_p, POINTER(c_int)]
    _DCONF_LIB.dconf_client_list.restype = POINTER(c_char_p)