Matt
Matt

Reputation: 1445

Find the default application name for a given file

In Linux, is there a way to ask any xdg services, or gtk services, which application is the default application for a given file?

I realize that xdg-open will in fact, launch the correct application. However, I want to be able to display the application's name in a context menu. So that when the user clicks on the menu item, it will then launch xdg-open, which will launch that app.

On OSX I can use LaunchServices for this:

def getDefaultDarwinApplication(path):
    import LaunchServices
    import CoreData
    import urllib

    url = CoreData.CFURLRef.URLWithString_("file://"+urllib.quote(path))
    os_status, app_ref, appurl = LaunchServices.LSGetApplicationForURL(url, LaunchServices.kLSRolesAll, None, None)
    if os_status != 0:
        return ""
    apppath = app_ref.as_pathname()
    name = os.path.basename(apppath).replace(".app", "")
    return name

The hope is that there is something similar on Linux I can use. A builtin python module would be best, but even screen scraping would work.

Upvotes: 7

Views: 2200

Answers (3)

Matt
Matt

Reputation: 1445

Even better I found the official gnome way to do this.

import gio
mime_type = gio.content_type_guess('foo.txt')
app_infos = gio.gio.app_info_get_all_for_type(mime_type)
for app_info in app_infos:
    print app_info.get_name(), app_info.get_executable(), app_info.get_icon()
# pure bliss

Upvotes: 3

Matt
Matt

Reputation: 1445

Here's a slightly more compact version using some methods from the official xdg python module and using some of the great code from @nosklo:

import os
import subprocess

from xdg import BaseDirectory
from xdg import DesktopEntry

class XDGError(Exception): pass

def xdg_query(command, parameter):
    p = subprocess.Popen(['xdg-mime', 'query', command, parameter],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, errors = p.communicate()
    if p.returncode or errors:
        raise XDGError('xdg-mime returned error code %d: %s' %
            (p.returncode, errors.strip()))
    return output.strip()


def get_default(filename):
    try:
        filetype = xdg_query('filetype', filename)
        desktop_filename = xdg_query('default', filetype)
    except XDGError:
        return None

    try:
        res = BaseDirectory.load_data_paths('applications', desktop_filename)
        if not res:
           return None
        desktop_file = res.next()
    except StopIteration:
        return None

    if not os.path.exists(desktop_file):
        return None
    return DesktopEntry.DesktopEntry(desktop_file)

And an example of how to use it:

entry = get_default("index.html")
if not entry:
    print "no default application found"
else:
    application_name = entry.getName()
    application_path = entry.getExec()
    application_icon = entry.getIcon()

Upvotes: 1

nosklo
nosklo

Reputation: 222862

Use the xdg-mime command. It allows you to query for a mimetype, and then get the program associated, without executing it.

Note that this returns the name of the associated .desktop file. Then you have to locate the actual file and further parse it to get the real name of the program, even localized in any language you want, path of the binary in the disk, etc.

Here's the full code:

import os
import subprocess
import codecs
import ConfigParser

class XDGError(Exception): pass
class FileNotFoundError(Exception): pass

def _get_app_paths():
    paths = os.environ.get('XDG_DATA_HOME', 
        os.path.expanduser('~/.local/share/')).split(os.path.pathsep)
    paths.extend(os.environ.get('XDG_DATA_DIRS', 
        '/usr/local/share/:/usr/share/').split(os.path.pathsep))
    return paths

def xdg_query(command, parameter):
    p = subprocess.Popen(['xdg-mime', 'query', command, parameter], 
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, errors = p.communicate()
    if p.returncode or errors:
        raise XDGError('xdg-mime returned error code %d: %s' % 
            (p.returncode, errors.strip()))
    return output.strip()

def locate_desktop_file(filename, mode='r', encoding='utf-8', 
        _paths=_get_app_paths()):
    for path in _paths:
        for thispath, dirs, files in os.walk(os.path.join(path, 'applications')):
            if filename not in files:
                continue
            fullname = os.path.join(thispath, filename)
            try:
                return codecs.open(fullname, mode, encoding)
            except IOError:
                pass
    else:
        raise FileNotFoundError(filename)

def get_defaults(filename):
    filetype = xdg_query('filetype', filename)
    desktop_filename = xdg_query('default', filetype)
    with locate_desktop_file(desktop_filename) as desktop_file:
        parser = ConfigParser.ConfigParser()
        parser.readfp(desktop_file, desktop_filename)
    return dict(parser.items(parser.sections()[0]))

Example usage:

p = get_defaults('index.html')
print p['name'], p['comment']

Upvotes: 8

Related Questions