goobook / goobook /

Full commit
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# vim: fileencoding=UTF-8 filetype=python ff=unix et ts=4 sw=4 sts=4 tw=120
# author: Christer Sjöholm -- hcs AT furuvik DOT net

from __future__ import absolute_import

import ConfigParser
import getpass
import logging
import netrc
import os
import subprocess

from import Storage
from os.path import realpath, expanduser

log = logging.getLogger(__name__)

# "#" or ";" at the start of a line makes it a comment.
# If not given here, email and password is taken from .netrc using
# machine
;password: top secret
# The following are optional, defaults are shown
;cache_filename: ~/.goobook_cache
;cache_expiry_hours: 24
;filter_groupless_contacts: yes

def read_config(config_file):
    '''Reads the ~/.goobookrc and ~/.netrc.
    returns the configuration as a dictionary.

    config = Storage({ # Default values
        'email': '',
        'password': '',
        'cache_filename': '~/.goobook_cache',
        'cache_expiry_hours': '24',
        'filter_groupless_contacts': True,
    config_file = os.path.expanduser(config_file)
    parser = _get_config(config_file)
    if parser:
        config.get_dict().update(dict(parser.items('DEFAULT', raw=True)))
        #Handle not string fields
        if parser.has_option('DEFAULT', 'filter_groupless_contacts'):
            config.filter_groupless_contacts = parser.getboolean('DEFAULT', 'filter_groupless_contacts')

    if and not config.password:'email present but password not, checking keyring...')
        import keyring
        config.password = keyring.get_password('gmail',
      except ImportError:

    if not or not config.password:
        netrc_file = os.path.expanduser('~/.netrc')
        if os.path.exists(netrc_file):
  'email or password missing from config, checking .netrc')
                auth = netrc.netrc(netrc_file).authenticators('')
            except netrc.NetrcParseError, err:
                raise ConfigError(err)
            if auth:
                login = auth[0]
                password = auth[2]
                if not
           = login
                if not config.password:
                    config.password = password
      'No match in .netrc')

    if not
        raise ConfigError('No email given in configfile or .netrc')
    if not config.password:
        raise ConfigError('No password could be found using any of the configuration alternatives')

    #replace password field with a function.
    if config.password == 'prompt':
        config.password = _password_prompt
        password = config.password
        config.password = lambda: password

    # Ensure paths are fully expanded
    config.cache_filename = realpath(expanduser(config.cache_filename))
    return config

def _password_prompt():
    password = ''
    while not password:
        password = getpass.getpass()
    return password

def _get_config(config_file):
    '''find, read and parse configuraton.'''
    parser = ConfigParser.SafeConfigParser()
    if os.path.lexists(config_file) and os.access(config_file, os.X_OK):
  'Executing config generator: %s', config_file)
            sub = subprocess.Popen([config_file], stdout=subprocess.PIPE)
            inp = sub.stdout
            return parser
        except (OSError, ), err:
            raise ConfigError("Failed to execute configuration generator: %s -- %s" % (config_file, err))
        except (IOError, ConfigParser.ParsingError), err:
            raise ConfigError("Failed to parse configuration generated by: %s -- %s" % (config_file, err))
    elif os.path.lexists(config_file):
  'Reading config: %s', config_file)
            inp = open(config_file)
            return parser
        except (IOError, ConfigParser.ParsingError), err:
            raise ConfigError("Failed to read configuration %s\n%s" % (config_file, err))
    return None

class ConfigError(StandardError):