銀月の符号

Python 使い見習いの日記・雑記

Firefox のプロファイルフォルダを探す

Firefox のプロファイルフォルダを探す Python の関数 get_profile_path(profile_name=None) を作ったので晒してみる。

もう一工夫すれば Thunderbird, SeaMonkey らにも対応できるかも。

Firefox 3 の クッキーを読み出そうとしていたときの副産物。クッキーを保持している cookies.sqlite のパスを直打ちしないために profiles.ini を探したり読んだりでしていたら、これが出来上がった。

os.path.join(get_profile_path(), u'cookies.sqlite')

といった感じで使っている。

コード

5月7日現在、 Mac, Linux での動作は未確認。os.path.expanduser でチルダ展開しないと動かない? Linux での確認予定はあり。 Mac は用意できないため、確認できないと思う。

# coding: utf-8
u"""Firefox のプロファイルフォルダを探す

参考:
    http://kb.mozillazine.org/Profiles.ini_file
    http://kb.mozillazine.org/Profile_folder
    http://kb.mozillazine.org/Profile_folder_-_Firefox
"""

import os
import ConfigParser

def get_profiles_folder():
    u"""profiles.ini ファイルのあるフォルダを得る"""
    if os.name == u'nt':
        profiles_folder = ur'%(AppData)s\Mozilla\Firefox' % os.environ
        if not os.path.isdir(profiles_folder):
            raise IOError(u'Profile folder is not found')
    elif os.name == u'mac':
        #XXX: 動作未確認
        profiles_folders = (
                u'~/Library/Mozilla/Firefox',
                u'~/Library/Application Support/Firefox')
        for d in profiles_folders:
            if os.path.isdir(d):
                profiles_folder = d
                break
        else:
            raise IOError(u'Profile folder is not found')
    else:
        #XXX: 動作未確認
        profiles_folder = u'~/.mozilla/firefox'
        if not os.path.isdir(profiles_folder):
            raise IOError(u'Profile folder is not found')
    return profiles_folder

def get_profiles_ini_path():
    u"""profiles.ini ファイルのパスを得る"""
    profiles_ini_path = os.path.join(
            get_profiles_folder(), u'profiles.ini')
    if not os.path.isfile(profiles_ini_path):
        raise IOError(u'profiles.ini is not found')
    return profiles_ini_path

def get_profile(profile_name=None):
    u"""profiles.ini より profile_name の設定を読み出す"""
    parser = ConfigParser.SafeConfigParser()
    parser.read(get_profiles_ini_path())

    if profile_name:
        # プロファイル名 profile_name の指定がある、これを探す
        sections = [s for s in parser.sections() if s != u'General']
        for section in sections:
            if parser.has_option(section, u'Name'):
                if parser.get(section, u'Name'):
                    section_name = section
                    break
        else:
            raise IOError(u'%r is not section' % profile_name)
    else:
        # プロファイル名 profile_name の指定が無い、デフォルトを探す
        sections = [s for s in parser.sections() if s != u'General']
        if len(sections) == 1:
            # プロファイルが 1 つだけの時、これを用いる
            section_name = sections[0]
        else:
            # 複数あるとき、 Defalut=1 を持つプロファイルを探す
            for section in sections:
                if parser.has_option(section, u'Default') and \
                   parser.getboolean(section, u'Default'):
                       section_name = section
                       break
            else:
                raise IOError(u'The default profile is not found')

    profile = dict(parser.items(section_name))
    for key in ('IsRelative', 'Default'):
        key = key.lower()
        if key in profile:
            profile[key] = int(profile[key])
    return profile

def get_profile_path(profile_name=None):
    u"""profile_name プロファイルが使用しているフォルダを返す"""
    profile = get_profile(profile_name)
    if profile[u'IsRelative'.lower()]:
        profile_dir = os.path.join(
                get_profiles_folder(),
                profile[u'Path'.lower()],)
    else:
        profile_dir = profile[u'Path']
    return os.path.normpath(profile_dir)

自分用メモ os.expandvars

posixpath.expandvars (Linux などの os.expandvars) は存在しない環境変数 ${name} をそのまま返す。ntpath.expandvars (Windows 上での os.expandvars) は ${name} を削除する。

どちらにせよ、存在しない環境変数を見つけても例外が起こったりはしない。あたりまえの動作といわれればそれまで。

存在しないとき動作を変えたければ '%(name)s' % os.environ で KeyError を起こす。もしくはめんどくさがらずに 'name' in os.environ できちんと調べる。