Accessing User Data

Each application has access to a user data directory that it can use to save user data separately from the application’s settings. Each application that uses this feature is expected to save its data in a suitably named subdirectory of the user data directory. This location is persistent and will be available to the application each time it is run.

In this example we use the directory to store data that is tightly coupled to the application.

Background

User data is different to application settings. User data is processed by the application to produce something for the user. Application settings change how the application behaves. Depending on the application, it can sometimes be difficult to clearly separate these types of information. It may help to consider how the application will function if the settings are reset to default values or if the user data is deleted. See Where to Store Data for guidance on this issue.

The kind of user data handled by this example is data that the user should not need to manage, so the user data directory is an ideal place to store the application’s data. Traditional data files that the user can load and save, or which are shared between applications, are stored elsewhere in the user’s account on the system, and this example is not concerned with those.

The location of the user data directory is defined by the XDG Base Directory Specification. The path to the directory is stored in the XDG_DATA_HOME environment variable. Applications can use library functions to obtain this at run-time instead of reading the environment variable.

Obtaining the User Data Directory

Applications use the GLib.get_user_data_dir function to obtain the location of the user data directory. In the example, the application does this in the do_startup method, storing the path to the directory in an attribute:

def do_startup(self):
    Gtk.Application.do_startup(self)

    self.data_subdir = os.path.join(GLib.get_user_data_dir(),
                                    'com.example.simple_weather')

Once the path to the data directory is known, a subdirectory can be created for later use if it does not already exist:

    try:
        if not os.path.exists(self.data_subdir):
            os.mkdir(self.data_subdir)

    except OSError:
        dialog = Gtk.MessageDialog(
            parent=None,
            flags=Gtk.DialogFlags.MODAL,
            message_type=Gtk.MessageType.ERROR,
            buttons=Gtk.ButtonsType.CLOSE,
            text=_('Cannot access data directory.')
        )
        dialog.run()
        self.quit()

In this case, the application shows a dialog and exits if the subdirectory cannot be created.

Storing and Retrieving User Data

Each application can create its own subdirectory in the user data directory and use it to store files. In this example we read and write files using regular file operations, but we also use the GLib.KeyFile class to store data as pairs of keys and values in the subdirectory created for the application’s use.

The application tries to read the contents of a key file, silently failing if no existing file can be read:

self.favorites_file = os.path.join(data_dir, 'favorites')
self.keyfile = GLib.KeyFile()

try:
    self.keyfile.load_from_file(
        self.favorites_file,
        GLib.KeyFileFlags.KEEP_COMMENTS |
        GLib.KeyFileFlags.KEEP_TRANSLATIONS
    )

    for favorite in self.keyfile.get_string_list('General',
                                                 'Favorites'):
        name, country, place = favorite.split('\t')
        self.model.append([name, country, place])

except GLib.Error:
    pass

This looks in the General section of the file and reads the value of the Favorites key as a list of strings.

Writing data to the file is done using a simple method that updates the General section of the keyfile, writing the value for the Favorites key as a semicolon-separated list of strings:

self.keyfile.set_string_list('General', 'Favorites', favorites)

try:
    self.keyfile.save_to_file(self.favorites_file)
except GLib.Error:
    # Cannot save the favorites. Maybe report this to the user.
    pass

GLib.KeyFile is convenient for storing and retrieving small amounts of simple structured data. Developers should use the APIs and libraries that make sense for the kinds of user data they need to manage.

Permissions

If the application is packaged as a flatpak, it can access the user data directory without needing to specify any permissions in the manifest. This is because each application only has access to its own user data directory in its sandbox – no other subdirectories are visible to it.

Although it is possible to write to the user data directory, applications should only create a subdirectory there and store data inside that. They should not rely on having access to other subdirectories. Taking a strict approach like this prevents problems with applications in the case where they are run outside a sandbox and can access a user data directory that is shared between many applications.

This example does not need to use files that can be imported or exported, or those that are shared between several applications. Access to the directories where those files are stored would require additional permissions. For example, the Pictures example uses an extra permission so that it can access the user’s Pictures directory.