Skip to content

Commit

Permalink
Merge branch '0.3.6-feature-abooks' into testing
Browse files Browse the repository at this point in the history
  • Loading branch information
pazz committed Apr 10, 2015
2 parents 2e06a29 + d76de3b commit 2c29bf7
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 120 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
master:
* switched to setuptools
* detached urwidtrees library into separate project (and new dependency)

0.3.6:
* implement vim-style "move last" command (bound to G)
* fixes in reply/forwarding
Expand Down
2 changes: 1 addition & 1 deletion alot/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Account(object):
signature_as_attachment = None
"""attach signature file instead of appending its content to body text"""
abook = None
"""addressbook (:class:`addressbooks.AddressBook`)
"""addressbook (:class:`addressbook.AddressBook`)
managing this accounts contacts"""

def __init__(self, address=None, aliases=None, realname=None,
Expand Down
38 changes: 38 additions & 0 deletions alot/addressbook/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
# This file is released under the GNU GPL, version 3 or a later revision.
# For further details see the COPYING file
import re


class AddressbookError(Exception):
pass


class AddressBook(object):
"""can look up email addresses and realnames for contacts.
.. note::
This is an abstract class that leaves :meth:`get_contacts`
unspecified. See :class:`AbookAddressBook` and
:class:`MatchSdtoutAddressbook` for implementations.
"""
def __init__(self, ignorecase=True):
self.reflags = re.IGNORECASE if ignorecase else 0

def get_contacts(self):
"""list all contacts tuples in this abook as (name, email) tuples"""
return []

def lookup(self, query=''):
"""looks up all contacts where name or address match query"""
res = []
query = '.*%s.*' % query
for name, email in self.get_contacts():
try:
if re.match(query, name, self.reflags) or \
re.match(query, email, self.reflags):
res.append((name, email))
except:
pass
return res
31 changes: 31 additions & 0 deletions alot/addressbook/abook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
# This file is released under the GNU GPL, version 3 or a later revision.
# For further details see the COPYING file

import os
from . import AddressBook
from ..settings.utils import read_config


class AbookAddressBook(AddressBook):
""":class:`AddressBook` that parses abook's config/database files"""
def __init__(self, path='~/.abook/addressbook', **kwargs):
"""
:param path: path to theme file
:type path: str
"""
AddressBook.__init__(self, **kwargs)
DEFAULTSPATH = os.path.join(os.path.dirname(__file__), 'defaults')
self._spec = os.path.join(DEFAULTSPATH, 'abook_contacts.spec')
path = os.path.expanduser(path)
self._config = read_config(path, self._spec)
del(self._config['format'])

def get_contacts(self):
c = self._config
res = []
for id in c.sections:
for email in c[id]['email']:
if email:
res.append((c[id]['name'], email))
return res
70 changes: 70 additions & 0 deletions alot/addressbook/external.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
# This file is released under the GNU GPL, version 3 or a later revision.
# For further details see the COPYING file
import re

from ..helper import call_cmd
from ..helper import split_commandstring
from . import AddressBook, AddressbookError


class ExternalAddressbook(AddressBook):
""":class:`AddressBook` that parses a shell command's output"""

def __init__(self, commandline, regex, reflags=None,
external_filtering=True,
**kwargs):
"""
:param commandline: commandline
:type commandline: str
:param regex: regular expression used to match contacts in `commands`
output to stdout. Must define subparts named "email" and
"name".
:type regex: str
:param reflags: flags to use with regular expression
:param external_filtering: if True the command is fired
with the given search string as parameter
and the result is not filtered further.
If set to False, the command is fired without
additional parameters and the result list is filtered
according to the search string.
:type external_filtering: bool
"""
AddressBook.__init__(self, **kwargs)
self.commandline = commandline
self.regex = regex
self.reflags = reflags
self.external_filtering = external_filtering

def get_contacts(self):
return self._call_and_parse(self.commandline)

def lookup(self, prefix):
if self.external_filtering:
return self._call_and_parse(self.commandline + " " + prefix)
else:
return AddressBook.lookup(self, prefix)

def _call_and_parse(self, commandline):
cmdlist = split_commandstring(commandline)
resultstring, errmsg, retval = call_cmd(cmdlist)
if retval != 0:
msg = 'abook command "%s" returned with ' % commandline
msg += 'return code %d' % retval
if errmsg:
msg += ':\n%s' % errmsg
raise AddressbookError(msg)

if not resultstring:
return []
lines = resultstring.splitlines()
res = []
for l in lines:
m = re.match(self.regex, l, self.reflags)
if m:
info = m.groupdict()
if 'email' and 'name' in info:
email = info['email'].strip()
name = info['name']
res.append((name, email))
return res
115 changes: 0 additions & 115 deletions alot/addressbooks.py

This file was deleted.

2 changes: 1 addition & 1 deletion alot/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from alot.settings import settings
from alot.utils.booleanaction import BooleanAction
from alot.helper import split_commandline
from alot.addressbooks import AddressbookError
from alot.addressbook import AddressbookError
from errors import CompletionError


Expand Down
7 changes: 7 additions & 0 deletions alot/defaults/alot.rc.spec
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,10 @@ prefer_plaintext = boolean(default=False)

# contacts file used for type 'abook' address book
abook_contacts_file = string(default='~/.abook/addressbook')

# (shellcommand addressbooks)
# let the external command do the filtering when looking up addresses.
# If set to True, the command is fired with the given search string
# as parameter. Otherwise, the command is fired without additional parameters
# and the result list is filtered according to the search string.
shellcommand_external_lookups = boolean(default=True)
9 changes: 6 additions & 3 deletions alot/settings/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from configobj import ConfigObj, Section

from alot.account import SendmailAccount
from alot.addressbooks import MatchSdtoutAddressbook, AbookAddressBook
from alot.addressbook.abook import AbookAddressBook
from alot.addressbook.external import ExternalAddressbook
from alot.helper import pretty_datetime, string_decode

from errors import ConfigError
Expand Down Expand Up @@ -127,8 +128,10 @@ def _parse_accounts(self, config):
cmd = abook['command']
regexp = abook['regexp']
if cmd is not None and regexp is not None:
args['abook'] = MatchSdtoutAddressbook(cmd,
match=regexp)
ef = abook['shellcommand_external_lookups']
args['abook'] = ExternalAddressbook(cmd,
regexp,
external_filtering=ef)
else:
msg = 'underspecified abook of type \'shellcommand\':'
msg += '\ncommand: %s\nregexp:%s' % (cmd, regexp)
Expand Down
20 changes: 20 additions & 0 deletions docs/source/configuration/contacts_completion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ Both respect the `ignorecase` option which defaults to `True` and results in cas
command = nottoomuch-addresses.sh
regexp = \"(?P<name>.+)\"\s*<(?P<email>.*.+?@.+?)>
`notmuch-abook <https://github.com/guyzmo/notmuch-abook>`_
completes contacts found in database of notmuch-abook:

.. code-block:: ini
command = notmuch_abook.py lookup
regexp = ^((?P<name>[^(\\s+\<)]*)\s+<)?(?P<email>[^@]+?@[^>]+)>?$
`notmuch address`
Since version `0.19`, notmuch itself offers a subcommand `address`, that
returns email addresses found in the notmuch index.
Combined with the `date:` syntax to query for mails within a certain
timeframe, this allows to search for all recently used contacts:

.. code-block:: ini
command = "notmuch address --output=recipients date:1Y.. AND from:my@address.org"
regexp = (\"?(?P<name>.+)\"?)?\s*<(?P<email>.*@.+?)>
shellcommand_external_lookups = False
Don't hesitate to send me your custom `regexp` values to list them here.

.. describe:: abook
Expand Down

0 comments on commit 2c29bf7

Please sign in to comment.