GIO tutorial: File operations

Recently I ported Exaile’s Files panel to use GIO instead of Python’s os module. The reason is simple: remote browsing.

Yes, you can now browse an SMB share or an SFTP server through the Files panel. Just mount the remote filesystem with gvfs-mount and enter the URL into the location bar. Previously Aren ported Exaile’s core to GIO as well, so remote files can be played just fine.

This is the kind of convenience you get out of using GIO/GVFS. In this article I’ll briefly explain what GIO is and how to do some basic file operations—similar to what the Files panel does—using GIO.

Screenshot of Exaile browsing and playing a file through GIO/GVFS

Note: This article is part of my ongoing GIO tutorial series, which currently consists of: (1) File and path operations (this document); (2) File IO; and (3) Launching files.

What is GIO? GVFS? GnomeVFS?

GIO is an input/output framework. It provides a bunch of functionalities related to files, directories, removable media, networks, and other stuff that fall under the IO category. This article’s focus—file operations—is just one of its features.

GVFS complements GIO by letting it access remote/virtual filesystems (the VFS part of the name).

GnomeVFS is an older technology that GIO and GVFS together replace.

GIO is distributed as part of GLib, while GVFS sits in a separate package. GnomeVFS was part of GNOME and is being phased out.

GFile, the bread and butter of file operations

This is the file class in GIO. By itself GFile represents nothing more than a URI, but it lets you do all sorts of operations on a file or directory. Here’s a simple example:

>>> import gio
>>> f = gio.File('.')
>>> f.get_uri()
file:///home/user
>>> f.get_path()
/home/user
>>> f.delete()
Traceback (most recent call last):
  ...
gio.Error: Error removing file: Directory not empty

Check the documentation (C reference, Python reference) to see other operations you can do with GFile.

GFileInfo

GFileInfo represents various properties of a file or stream, such as size, permissions, etc. To obtain the GFileInfo of a file, call GFile’s query_info method.

>>> f = gio.File('.')
>>> info = f.query_info('standard::type,standard::size')
>>> info.get_file_type()
<enum G_FILE_TYPE_DIRECTORY of type GFileType>
>>> info.get_size()
0L

query_info requires a list of attributes that you want to retrieve. Only the specified attributes are then populated into the GFileInfo. Multiple attributes can be queried by separating them with commas. Wildcards in the form of * and namespace::* are also accepted.

In this example, we query the file type and size (by the way, the size is always 0 for directories). The results are stored in the returned GFileInfo.

To retrieve a value from GFileInfo, call its get_attribute_* methods (e.g. get_attribute_uint32("standard::type")). Some attributes have special getters (e.g. get_file_type).

A complete list of file attributes is available. The various attribute getter methods are detailed in the GFileInfo documentation (C reference, Python reference).

Example: getting parent directory

This is just a very simple example showing how to get the parent of a directory.

import gio
current = gio.File('.')
parent = current.get_parent()

Example: getting contents of a directory

This example shows the meat of the Files panel: displaying the subdirectories and files inside a directory. For each file, we also obtain its size.

import gio

current = gio.File('.')
subdirs = []
files = []

infos = current.enumerate_children(
        'standard::name,standard::type,standard::size')
for info in infos:
    child = current.get_child(info.get_name())
    if info.get_file_type() == gio.FILE_TYPE_DIRECTORY:
        subdirs.append(child)
    else:
        files.append((child, info.get_size()))

Notice that enumerate_children works similarly to query_info, but it retrieves the properties of all the children of a directory.

What else?

That’s it, we’ve covered everything essential for a file browser. The Files panel just builds a GUI and a history feature on top.

I’ve written a small demonstration of a working GIO file browser; try giving it a remote location to browse (don’t forget to gvfs-mount the filesystem first).

Screenshot of local GIO file browsing

Screenshot of "remote" GIO file browsing

8 thoughts on “GIO tutorial: File operations

  1. Stu

    This is cool, how about if I want to open a zip (.jar) file and enumerate it’s children?

    Heres what I tried:

    >>> z = gio.File('jsr082.jar')
    >>> infos = z.enumerate_children('standard::name,standard::type,standard::size')
    Traceback (most recent call last):
      File "", line 1, in 
    glib.GError: Error opening directory 'C:\usr\WTK2.5.2\lib\jsr082.jar': Not a directory
    
    1. Johannes Post author

      That’s quite tricky; I don’t think GIO/GVFS supports opening zipped files as directories per se. What you can do is mount the zip file with GIO, and then access the file as if you’re accessing a remote GIO mount.

      I’ll give it a try later. Might actually be a good topic for another post if I can get it working.

  2. Pingback: Mount berkas arsip dengan GIO « Dari Tiada Ke Tiada

  3. Romano

    Hi, I am trying to remove an attribute (“metadata::emblems”) using the call


    mydir=gio.File("./tmp")
    mydir.set_attribute("metadata::emblems", gio.FILE_ATTRIBUTE_TYPE_STRINGV, ["new", "hot"])

    and it works. The problem is that I cannot find how to unset it. In the C call g_file_set_attribute you pass a NULL as the value, but in the python version I can’t pass a None vale. And if I pass an empty array the attribute is set to the empty array… Have you any hint? Thanks!

    1. Johannes Sasongko Post author

      This does seem to be a PyGTK oversight—I can’t find a way to do it either. But the bigger problem is that unsetting an attribute is not documented even in the GIO docs.

      You may want to file bug reports on both GLib and PyGTK. (And if you do, please share the links here.)

      In the meantime, you can work around this by running gvfs-set-attribute -t unset path metadata::emblems or using ctypes.

  4. Romano

    I l-traced the gvfs-set-attribute command, and it seems that to remove the attribute (from C) you simply call the set_attribute() function passing a NULL pointer.

    Unfortunately, it does not work in Python, because it refuses a “None” argument. I do not know how to use ctype… I sent a message to the pygtk-python-hacker list (awaiting moderation because I am not a subscriber).

    If I solve something, I’ll post here. Spawning gvfs-set-attribute is a solution, but I need to spawn a process (I was trying to write a tool to keep emblems synchronized between teo rsync-ed trees), and it is not… elegant.

Comments are closed.