RkBlog

Hardware, programming and astronomy tutorials and reviews.

pyxdg - freedesktop.org specifications support

Handle application menu, file MIME types, recently used files, .desktop files and more used by Linux/Unix window managers.

pyxdg is a Python library that handles freedesktop.org standards for common desktop operations and components like determining file MIME type, getting icon for a specific file or application, accessing application menu and more. Most Linux/Unix window managers use those specifications (except KDE3 which supports them partially).

Determining file MIMEtype

xdg.Mime module allows us to determine file mime type very accurately using magic number tests. Advantage is that if a file has wrong extension - the test will return correct MIME. Python standard library has also a module called "mimetypes", but it works using lists of mapped file extensions. Here is a code example:
from os import listdir
import xdg.Mime

files = listdir('files')
for f in files:
	print '%s: %s' % (f, xdg.Mime.get_type('files/%s' % f, name_pri=0))

print

for f in files:
	print '%s: %s' % (f, xdg.Mime.get_type_by_name('files/%s' % f))

print

import mimetypes
mimetypes.init()
for f in files:
	ext = u'.%s' % f.split('.')[-1]
	try:
		ext = mimetypes.types_map[ext]
		print '%s: %s' % (f, ext)
	except:
		print '%s: None' % f
Function get_type can use magic number test (if name_pri set to less than 100) to determine the MIME of a file. Function get_type_by_name uses filename to guess the mime. Sample result can look like this:
file.sql: text/x-sql
plikrpm: application/x-rpm
file.c: text/x-csrc
file.yml: text/plain
file.htm: text/html
file.html: text/html
file.h: text/x-chdr
file.cs: text/x-csharp
file.pl: application/x-perl
file.fortran: text/plain
file.js: application/javascript
file.css: text/css
file.sh: application/x-shellscript
file.py: text/x-python
file.xml: application/xml
file.kaffeine: text/plain
file.rb: application/x-ruby
plikrpm.txt: application/x-rpm
file.diff: text/x-patch
file.d: text/x-dsrc
file.tk: text/x-tcl
file.php: application/x-php
file.java: text/x-java
file.cpp: text/x-c++src
file.tcl: text/x-tcl
file.patch: text/x-patch
plik.rpm: application/x-rpm

file.sql: text/x-sql
plikrpm: None
file.c: text/x-csrc
file.yml: None
file.htm: text/html
file.html: text/html
file.h: text/x-chdr
file.cs: text/x-csharp
file.pl: application/x-perl
file.fortran: None
file.js: application/javascript
file.css: text/css
file.sh: application/x-shellscript
file.py: text/x-python
file.xml: application/xml
file.kaffeine: None
file.rb: application/x-ruby
plikrpm.txt: text/plain
file.diff: text/x-patch
file.d: text/x-dsrc
file.tk: text/x-tcl
file.php: application/x-php
file.java: text/x-java
file.cpp: text/x-c++src
file.tcl: text/x-tcl
file.patch: text/x-patch
plik.rpm: application/x-rpm

file.sql: None
plikrpm: None
file.c: text/x-csrc
file.yml: None
file.htm: text/html
file.html: text/html
file.h: text/x-chdr
file.cs: None
file.pl: text/x-perl
file.fortran: None
file.js: application/x-javascript
file.css: text/css
file.sh: text/x-sh
file.py: text/x-python
file.xml: application/xml
file.kaffeine: None
file.rb: None
plikrpm.txt: text/plain
file.diff: text/plain
file.d: None
file.tk: text/x-tcl
file.php: None
file.java: text/x-java
file.cpp: text/x-c++src
file.tcl: text/x-tcl
file.patch: None
plik.rpm: application/x-redhat-package-manager
All "file.*" are empty files. "plik.*" files are RPM package (note incorrect .txt extension, and no extension at all).

Base Directories

xdg.BaseDirectory module implements Base Directory Specification - a set of variables determining directories for data, configs and cache. xdg.BaseDirectory module offers easy access to those variables:
import xdg.BaseDirectory as bd

print 'xdg_data_home: %s' % bd.xdg_data_home
print 'xdg_data_dirs: %s' % bd.xdg_data_dirs
print 'xdg_config_home: %s' % bd.xdg_config_home
print 'xdg_config_dirs: %s' % bd.xdg_config_dirs
print 'xdg_cache_home: %s' % bd.xdg_cache_home
Sample output:
xdg_data_home: /home/piotr/.local/share
xdg_data_dirs: ['/home/piotr/.local/share', '/usr/kde/3.5/share', '/usr/share', '/usr/local/share']
xdg_config_home: /home/piotr/.config
xdg_config_dirs: ['/home/piotr/.config', '/usr/kde/3.5/etc/xdg']
xdg_cache_home: /home/piotr/.cache

.desktop files

xdg.DesktopEntry modules supply us with a class that implements XDG Desktop Entry Specification. Desktop files are used to put an application in the system application menu. Example:
import xdg.DesktopEntry as d

de = d.DesktopEntry(filename='openarena.desktop')
print 'getType: %s' % de.getType()
print 'getVersion: %s' % de.getVersion()
print 'getEncoding: %s' % de.getEncoding()
print 'getName: %s' % de.getName()
print 'getGenericName: %s' % de.getGenericName()
print 'getComment: %s' % de.getComment()
print 'getNoDisplay: %s' % de.getNoDisplay()
print 'getIcon: %s' % de.getIcon()
print 'getHidden: %s' % de.getHidden()
print 'getFilePattern: %s' % de.getFilePattern()
print 'getTryExec: %s' % de.getTryExec()
print 'getExec: %s' % de.getExec()
print 'getPath: %s' % de.getPath()
print 'getTerminal: %s' % de.getTerminal()
print 'getSwallowTitle: %s' % de.getSwallowTitle()
print 'getSwallowExec: %s' % de.getSwallowExec()
print 'getActions: %s' % de.getActions()
print 'getMimeType: %s' % de.getMimeType()
print 'getSortOrder: %s' % de.getSortOrder()
print 'getDev: %s' % de.getDev()
print 'getFSType: %s' % de.getFSType()
print 'getMountPoint: %s' % de.getMountPoint()
print 'getReadonly: %s' % de.getReadonly()
print 'getUnmountIcon: %s' % de.getUnmountIcon()
print 'getURL: %s' % de.getURL()
print 'getCategories: %s' % de.getCategories()
print 'getOnlyShowIn: %s' % de.getOnlyShowIn()
print 'getNotShowIn: %s' % de.getNotShowIn()
print 'getStartupNotify: %s' % de.getStartupNotify()
print 'getStartupWMClass: %s' % de.getStartupWMClass()
print 'getServiceTypes: %s' % de.getServiceTypes()
print 'getDocPath: %s' % de.getDocPath()
print 'getKeywords: %s' % de.getKeywords()
print 'getInitialPreference: %s' % de.getInitialPreference()
getType: Application
getVersion: 0.0
getEncoding:
getName: OpenArena
getGenericName:
getComment: A Quake3-based FPS Game
getNoDisplay: False
getIcon: openarena
getHidden: False
getFilePattern: < _sre.SRE_Pattern object at 0x7f41f98cde70 >
getTryExec:
getExec: openarena
getPath:
getTerminal: False
getSwallowTitle:
getSwallowExec:
getActions: []
getMimeType: []
getSortOrder: []
getDev:
getFSType:
getMountPoint:
getReadonly: False
getUnmountIcon:
getURL:
getCategories: ['Game', 'ActionGame']
getOnlyShowIn: []
getNotShowIn: []
getStartupNotify: False
getStartupWMClass:
getServiceTypes: []
getDocPath:
getKeywords: []
getInitialPreference:

Icons and iconset support

Iconsets that are used for example in GNOME or KDE4 follow XDG Icon Spec. xdg.IconTheme module contains a class that implements that specification, and can read iconset informations, or return an icon for application or other element:
import xdg.IconTheme as ic

i = ic.IconTheme()
i.parse('/home/piotr/.kde/share/icons/KDEmod-Icons-Tango/index.theme')
print 'getName: %s' % i.getName()
print 'getComment: %s' % i.getComment()
print 'getInherits: %s' % i.getInherits()
print 'getDirectories: %s' % i.getDirectories()
print 'getHidden: %s' % i.getHidden()
print 'getExample: %s' % i.getExample()
print
print 'getSize: %s' % i.getSize('scalable/categories')
print 'getContext: %s' % i.getContext('scalable/categories')
print 'getType: %s' % i.getType('scalable/categories')
print 'getMaxSize: %s' % i.getMaxSize('scalable/categories')
print 'getMinSize: %s' % i.getMinSize('scalable/categories')
print 'getThreshold: %s' % i.getThreshold('scalable/categories')
That can give an output like:
getName: KDEmod-Icons-Tango
getComment: Tango and Tango-like icons for KDEmod
getInherits: ['crystalsvg']
getDirectories: ['16x16/actions', '16x16/apps', '16x16/categories', '16x16/devices', '16x16/mimetypes', '22x22/actions', '22x22/apps', '22x22/categories', '22x22/devices', '22x22/mimetypes', '32x32/actions', '32x32/apps', '32x32/categories', '32x32/devices', '32x32/mimetypes', '48x48/actions', '48x48/apps', '48x48/categories', '48x48/devices', '48x48/mimetypes', '64x64/actions', '64x64/apps', '64x64/categories', '64x64/devices', '64x64/mimetypes', '128x128/actions', '128x128/apps', '128x128/categories', '128x128/devices', '128x128/mimetypes', 'scalable/actions', 'scalable/apps', 'scalable/categories', 'scalable/devices', 'scalable/mimetypes', '16x16/filesystems', '22x22/filesystems', '32x32/filesystems', '48x48/filesystems', '64x64/filesystems', '128x128/filesystems']
getHidden: False
getExample: folder

getSize: 48
getContext: Categories
getType: Scalable
getMaxSize: 256
getMinSize: 32
getThreshold: 0
To get an icon for a application use:
print ic.getIconPath("opera")

Recently used files

XDG Recent File Storage Specification describes a file that contain informations about recently used files. xdg.RecentFiles module contains a class that can access such file, and even edit it. Here is an example:
import xdg.RecentFiles as rc

i = rc.RecentFiles()
i.parse()
files = i.getFiles()
for f in files:
	print f.URI
	print f.Timestamp
	print f.Groups
	print f.MimeType
	print
file:///home/piotr/nowe/GAE/echoo-google-app-engine-barcamp-saigon-1-1227167304763656-9.ppt
1227913909
[u'openoffice.org', u'staroffice', u'starsuite']
application/vnd.ms-powerpoint

file:///home/piotr/nowe/GAE/starpad-1220394698551686-8.ppt
1227913837
[u'openoffice.org', u'staroffice', u'starsuite']
application/vnd.ms-powerpoint

file:///home/piotr/nowe/GAE/ajaxworldwest-1224787694777812-9.ppt
1227913822
[u'openoffice.org', u'staroffice', u'starsuite']
application/vnd.ms-powerpoint

Applications Menu

KDE, GNOME and other WMs have their menu with all installed GUI applications. pyxdg has two modules: xdg.Menu and xdg.MenuEditor. the first one can list elements of the application menu, and the other one can edit it. Here is an example:
#!/usr/bin/python

import sys

import xdg.Menu
import xdg.DesktopEntry

def show_menu(menu, depth = 0):
	for entry in menu.getEntries():
		if isinstance(entry, xdg.Menu.Menu):
			show_menu(entry, depth)
		elif isinstance(entry, xdg.Menu.MenuEntry):
			print menu.getPath() + "/	" + entry.DesktopFileID + "	" + entry.DesktopEntry.getFileName()

show_menu(xdg.Menu.parse())
RkBlog

Python programming, 29 November 2008,

Comment article