summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpatrick <p.totzke@ed.ac.uk>2011-07-26 14:29:53 +0100
committerpatrick <p.totzke@ed.ac.uk>2011-07-26 14:29:53 +0100
commit375fe29e1859c13d4c61d04bc171678bbb4d96f0 (patch)
treed3e1fe94f1e651197333bccb1410d9033b09bcaf
parentd64475a92e472865d50f5ac6b7c0c39c6654ff5c (diff)
parentb4f761bd7afbd0f4302ed2c5bdedfe29a82383d0 (diff)
Merge branch 'restructure'
-rw-r--r--alot/account.py44
-rw-r--r--alot/commandfactory.py6
-rw-r--r--alot/commands.py25
-rw-r--r--alot/completion.py8
-rw-r--r--alot/db.py227
-rwxr-xr-xalot/init.py11
-rw-r--r--alot/message.py230
-rw-r--r--alot/settings.py125
-rw-r--r--alot/ui.py13
-rw-r--r--alot/widgets.py6
10 files changed, 374 insertions, 321 deletions
diff --git a/alot/account.py b/alot/account.py
index 9dfb6a2e..8e4c6f61 100644
--- a/alot/account.py
+++ b/alot/account.py
@@ -18,6 +18,7 @@ Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com>
"""
import mailbox
+import logging
from urlparse import urlparse
from send import SendmailSender
@@ -53,3 +54,46 @@ class Account:
if self.sender_type == 'sendmail':
self.sender = SendmailSender(sendmail_command,
mailbox=self.mailbox)
+
+
+class AccountManager:
+ allowed = ['realname',
+ 'address',
+ 'gpg_key',
+ 'signature',
+ 'sender_type',
+ 'sendmail_command',
+ 'sent_mailbox']
+ manditory = ['realname', 'address']
+ accounts = {}
+
+ def __init__(self, config):
+ sections = config.sections()
+ accountsections = filter(lambda s: s.startswith('account '), sections)
+ for s in accountsections:
+ options = filter(lambda x: x in self.allowed, config.options(s))
+ args = {}
+ for o in options:
+ args[o] = config.get(s, o)
+ if o in self.manditory:
+ self.manditory.remove(o)
+ if not self.manditory:
+ logging.info(args)
+ newacc = (Account(**args))
+ self.accounts[newacc.address] = newacc
+ else:
+ pass
+ # log info
+
+ def get_accounts(self):
+ return self.accounts.values()
+
+ def get_account_by_address(self, address):
+ if address in self.accounts:
+ return self.accounts[address]
+ else:
+ return None
+ # log info
+
+ def get_account_addresses(self):
+ return self.accounts.keys()
diff --git a/alot/commandfactory.py b/alot/commandfactory.py
index 985583be..0b653068 100644
--- a/alot/commandfactory.py
+++ b/alot/commandfactory.py
@@ -20,7 +20,7 @@ import logging
import os
-from settings import get_hook
+from settings import hooks
import commands
COMMANDS = {
@@ -70,8 +70,8 @@ def commandfactory(cmdname, **kwargs):
else:
parms[key] = value
- parms['prehook'] = get_hook('pre_' + cmdname)
- parms['posthook'] = get_hook('post_' + cmdname)
+ parms['prehook'] = hooks.get('pre_' + cmdname)
+ parms['posthook'] = hooks.get('post_' + cmdname)
logging.debug('cmd parms %s' % parms)
return cmdclass(**parms)
diff --git a/alot/commands.py b/alot/commands.py
index dde5fdf4..aeee2f33 100644
--- a/alot/commands.py
+++ b/alot/commands.py
@@ -29,10 +29,7 @@ from email.mime.text import MIMEText
import tempfile
import buffer
-from settings import config
-from settings import get_hook
-from settings import get_account_by_address
-from settings import get_accounts
+import settings
from db import DatabaseROError
from db import DatabaseLockedError
from completion import ContactsCompleter
@@ -158,7 +155,9 @@ class ExternalCommand(Command):
def thread_code(*args):
cmd = self.commandstring
if self.spawn:
- cmd = config.get('general', 'terminal_cmd') + ' ' + cmd
+ cmd = '%s %s' % (settings.config.get('general',
+ 'terminal_cmd'),
+ cmd)
ui.logger.info('calling external command: %s' % cmd)
returncode = subprocess.call(cmd, shell=True)
if returncode == 0:
@@ -179,8 +178,8 @@ class EditCommand(ExternalCommand):
if spawn != None:
self.spawn = spawn
else:
- self.spawn = config.getboolean('general', 'spawn_editor')
- editor_cmd = config.get('general', 'editor_cmd')
+ self.spawn = settings.config.getboolean('general', 'spawn_editor')
+ editor_cmd = settings.config.get('general', 'editor_cmd')
cmd = editor_cmd + ' ' + self.path
ExternalCommand.__init__(self, cmd, spawn=self.spawn,
in_thread=self.spawn,
@@ -293,7 +292,7 @@ class FlushCommand(Command):
try:
ui.dbman.flush()
except DatabaseLockedError:
- timeout = config.getint('general', 'flush_retry_timeout')
+ timeout = settings.config.getint('general', 'flush_retry_timeout')
def f(*args):
self.apply(ui)
@@ -353,7 +352,7 @@ class SendMailCommand(Command):
envelope = ui.current_buffer
mail = envelope.get_email()
sname, saddr = email.Utils.parseaddr(mail.get('From'))
- account = get_account_by_address(saddr)
+ account = settings.accounts.get_account_by_address(saddr)
if account:
success, reason = account.sender.send_mail(mail)
if success:
@@ -379,14 +378,14 @@ class ComposeCommand(Command):
def apply(self, ui):
# TODO: fill with default header (per account)
# get From header
- accounts = get_accounts()
+ accounts = ui.accountman.get_accounts()
if len(accounts) == 0:
ui.notify('no accounts set')
return
elif len(accounts) == 1:
a = accounts[0]
else:
- cmpl = AccountCompleter()
+ cmpl = AccountCompleter(ui.accountman)
fromaddress = ui.prompt(prefix='From>', completer=cmpl, tab=1)
validaddresses = [a.address for a in accounts] + [None]
while fromaddress not in validaddresses:
@@ -395,14 +394,14 @@ class ComposeCommand(Command):
if not fromaddress:
ui.notify('canceled')
return
- a = get_account_by_address(fromaddress)
+ a = ui.accountman.get_account_by_address(fromaddress)
self.headers['From'] = "%s <%s>" % (a.realname, a.address)
#get To header
if 'To' not in self.headers:
self.headers['To'] = ui.prompt(prefix='To>',
completer=ContactsCompleter())
- if config.getboolean('general', 'ask_subject') and \
+ if settings.config.getboolean('general', 'ask_subject') and \
not 'Subject' in self.headers:
self.headers['Subject'] = ui.prompt(prefix='Subject>')
diff --git a/alot/completion.py b/alot/completion.py
index fcfc4d57..84335698 100644
--- a/alot/completion.py
+++ b/alot/completion.py
@@ -86,8 +86,11 @@ class ContactsCompleter(Completer):
class AccountCompleter(Completer):
"""completes own mailaddresses"""
+ def __init__(self, accountman):
+ self.accountman = accountman
+
def complete(self, prefix):
- valids = settings.get_account_addresses()
+ valids = self.accountman.get_account_addresses()
return [a[len(prefix):] for a in valids if a.startswith(prefix)]
class CommandCompleter(Completer):
@@ -107,8 +110,9 @@ class CommandCompleter(Completer):
class CommandLineCompleter(Completer):
"""completion for commandline"""
- def __init__(self, dbman, mode):
+ def __init__(self, dbman, accoountman, mode):
self.dbman = dbman
+ self.accountman = accountman
self.mode = mode
self._commandcompleter = CommandCompleter(dbman, mode)
self._querycompleter = QueryCompleter(dbman)
diff --git a/alot/db.py b/alot/db.py
index a6aaac22..a6ccb4fc 100644
--- a/alot/db.py
+++ b/alot/db.py
@@ -18,18 +18,11 @@ Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com>
"""
from notmuch import Database, NotmuchError
from datetime import datetime
-import email
from collections import deque
-import os
-import logging
-import tempfile
-from settings import config
-from settings import get_mime_handler
-import helper
-from helper import cmd_output
+from message import Message
-DB_ENC = 'utf8'
+DB_ENC = 'utf-8'
class DatabaseError(Exception):
@@ -96,7 +89,8 @@ class DBManager:
sync_maildir_flags=sync)
msg.thaw()
- def tag(self, querystring, tags, remove_rest=False, sync_maildir_flags=False):
+ def tag(self, querystring, tags, remove_rest=False,
+ sync_maildir_flags=False):
"""
add tags to all matching messages. Raises
:exc:`DatabaseROError` if in read only mode.
@@ -112,9 +106,11 @@ class DBManager:
if self.ro:
raise DatabaseROError()
if remove_rest:
- self.writequeue.append(('set', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('set', querystring, tags,
+ sync_maildir_flags))
else:
- self.writequeue.append(('tag', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('tag', querystring, tags,
+ sync_maildir_flags))
def untag(self, querystring, tags, sync_maildir_flags=False):
"""
@@ -129,7 +125,8 @@ class DBManager:
"""
if self.ro:
raise DatabaseROError()
- self.writequeue.append(('untag', querystring, tags, sync_maildir_flags))
+ self.writequeue.append(('untag', querystring, tags,
+ sync_maildir_flags))
def count_messages(self, querystring):
"""returns number of messages that match querystring"""
@@ -181,12 +178,10 @@ class Thread:
if not thread:
query = self._dbman.query('thread:' + self._id)
thread = query.search_threads().next()
- logging.debug(thread)
self._total_messages = thread.get_total_messages()
self._authors = str(thread.get_authors()).decode(DB_ENC)
self._subject = str(thread.get_subject()).decode(DB_ENC)
ts = thread.get_oldest_date()
- logging.debug(ts)
self._oldest_date = datetime.fromtimestamp(ts)
self._newest_date = datetime.fromtimestamp(thread.get_newest_date())
self._tags = set(thread.get_tags())
@@ -302,205 +297,3 @@ class Thread:
def get_total_messages(self):
"""returns number of contained messages"""
return self._total_messages
-
-
-class Message:
- def __init__(self, dbman, msg, thread=None):
- """
- :param dbman: db manager that is used for further lookups
- :type dbman: alot.db.DBManager
- :param msg: the wrapped message
- :type msg: notmuch.database.Message
- :param thread: this messages thread
- :type thread: alot.db.thread
- """
- self._dbman = dbman
- self._id = msg.get_message_id()
- self._thread_id = msg.get_thread_id()
- self._thread = thread
- self._datetime = datetime.fromtimestamp(msg.get_date())
- self._filename = msg.get_filename()
- # TODO: change api to return unicode
- self._from = msg.get_header('From').decode(DB_ENC)
- self._email = None # will be read upon first use
- self._attachments = None # will be read upon first use
- self._tags = set(msg.get_tags())
-
- def __str__(self):
- """prettyprint the message"""
- aname, aaddress = self.get_author()
- if not aname:
- aname = aaddress
- #tags = ','.join(self.get_tags())
- return "%s (%s)" % (aname, self.get_datestring())
-
- def __hash__(self):
- """Implement hash(), so we can use Message() sets"""
- return hash(self._id)
-
- def __cmp__(self, other):
- """Implement cmp(), so we can compare Message()s"""
- res = cmp(self.get_message_id(), other.get_message_id())
- return res
-
- def get_email(self):
- """returns email.Message representing this message"""
- if not self._email:
- f_mail = open(self.get_filename())
- self._email = email.message_from_file(f_mail)
- f_mail.close()
- return self._email
-
- def get_date(self):
- """returns date as datetime obj"""
- return self._datetime
-
- def get_filename(self):
- """returns absolute path of messages location"""
- return self._filename
-
- def get_message_id(self):
- """returns messages id (a string)"""
- return self._id
-
- def get_thread_id(self):
- """returns id of messages thread (a string)"""
- return self._thread_id
-
- def get_message_parts(self):
- """returns a list of all body parts of this message"""
- out = []
- for msg in self.get_email().walk():
- if not msg.is_multipart():
- out.append(msg)
- return out
-
- def get_tags(self):
- """returns tags attached to this message as list of strings"""
- return list(self._tags)
-
- def get_thread(self):
- """returns the thread this msg belongs to as alot.db.Thread object"""
- if not self._thread:
- self._thread = self._dbman.get_thread(self._thread_id)
- return self._thread
-
- def get_replies(self):
- """returns a list of replies to this msg"""
- t = self.get_thread()
- return t.get_replies_to(self)
-
- def get_datestring(self, pretty=True):
- """returns formated datestring in sup-style, eg: 'Yest.3pm'"""
- return helper.pretty_datetime(self._datetime)
-
- def get_author(self):
- """returns realname and address pair of this messages author"""
- return email.Utils.parseaddr(self._from)
-
- def add_tags(self, tags):
- """adds tags to message
-
- :param tags: tags to add
- :type tags: list of str
- """
- self._dbman.tag('id:' + self._id, tags)
- self._tags = self._tags.union(tags)
-
- def remove_tags(self, tags):
- """remove tags from message
-
- :param tags: tags to remove
- :type tags: list of str
- """
- self._dbman.untag('id:' + self._id, tags)
- self._tags = self._tags.difference(tags)
-
- def get_attachments(self):
- if not self._attachments:
- self._attachments = []
- for part in self.get_message_parts():
- if part.get_content_maintype() != 'text':
- self._attachments.append(Attachment(part))
- return self._attachments
-
- def accumulate_body(self):
- bodytxt = ''
- for part in self.get_email().walk():
- ctype = part.get_content_type()
- enc = part.get_content_charset()
- raw_payload = part.get_payload(decode=True)
- if part.get_content_maintype() == 'text':
- if enc:
- raw_payload = unicode(raw_payload, enc)
- else:
- raw_payload = unicode(raw_payload, errors='replace')
- if ctype == 'text/plain':
- bodytxt += raw_payload
- else:
- #get mime handler
- handler = get_mime_handler(ctype, key='view',
- interactive=False)
- if handler:
- #open tempfile. Not all handlers accept stuff from stdin
- tmpfile = tempfile.NamedTemporaryFile(delete=False,
- suffix='.html')
- #write payload to tmpfile
- if part.get_content_maintype() == 'text':
- tmpfile.write(raw_payload.encode('utf8'))
- else:
- tmpfile.write(raw_payload)
- #create and call external command
- cmd = handler % tmpfile.name
- rendered_payload = cmd_output(cmd)
- #remove tempfile
- tmpfile.close()
- os.unlink(tmpfile.name)
- if rendered_payload: # handler had output
- bodytxt += unicode(rendered_payload.strip(),
- encoding='utf8', errors='replace')
- elif part.get_content_maintype() == 'text':
- bodytxt += raw_payload
- # else drop
- return bodytxt
-
-
-class Attachment:
- """represents a single mail attachment"""
-
- def __init__(self, emailpart):
- """
- :param emailpart: a non-multipart email that is the attachment
- :type emailpart: email.message.Message
- """
- self.part = emailpart
-
- def __str__(self):
- return '%s:%s (%s)' % (self.get_content_type(),
- self.get_filename(),
- self.get_size())
-
- def get_filename(self):
- """return the filename, extracted from content-disposition header"""
- return self.part.get_filename()
-
- def get_content_type(self):
- """mime type of the attachment"""
- return self.part.get_content_type()
-
- def get_size(self):
- """returns attachments size as human-readable string"""
- size_in_kbyte = len(self.part.get_payload()) / 1024
- if size_in_kbyte > 1024:
- return "%.1fM" % (size_in_kbyte / 1024.0)
- else:
- return "%dK" % size_in_kbyte
-
- def save(self, path):
- """save the attachment to disk. Uses self.get_filename
- in case path is a directory"""
- if os.path.isdir(path):
- path = os.path.join(path, self.get_filename())
- FILE = open(path, "w")
- FILE.write(self.part.get_payload(decode=True))
- FILE.close()
diff --git a/alot/init.py b/alot/init.py
index 9217bfba..c6eb839a 100755
--- a/alot/init.py
+++ b/alot/init.py
@@ -21,6 +21,7 @@ import logging
import os
import settings
+from account import AccountManager
from db import DBManager
from ui import UI
from urwid.command_map import command_map
@@ -59,7 +60,11 @@ def main():
#read config file
configfilename = os.path.expanduser(args.configfile)
- settings.setup(configfilename)
+ settings.config.read(configfilename)
+ settings.hooks.setup(settings.config.get('general', 'hooksfile'))
+
+ #accountman
+ aman = AccountManager(settings.config)
# setup logging
numeric_loglevel = getattr(logging, args.debug_level.upper(), None)
@@ -69,8 +74,6 @@ def main():
# get ourselves a database manager
dbman = DBManager(path=args.db_path, ro=args.read_only)
- # read accounts
- accounts = settings.get_accounts()
# set up global urwid command maps
command_map['j'] = 'cursor down'
@@ -82,7 +85,7 @@ def main():
# set up and start interface
ui = UI(dbman,
logger,
- accounts,
+ aman,
args.query,
args.colours,
)
diff --git a/alot/message.py b/alot/message.py
new file mode 100644
index 00000000..c165ab5e
--- /dev/null
+++ b/alot/message.py
@@ -0,0 +1,230 @@
+"""
+This file is part of alot.
+
+Alot is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+Alot is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with notmuch. If not, see <http://www.gnu.org/licenses/>.
+
+Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com>
+"""
+import os
+import email
+import tempfile
+from datetime import datetime
+
+import helper
+from settings import get_mime_handler
+
+
+class Message:
+ def __init__(self, dbman, msg, thread=None):
+ """
+ :param dbman: db manager that is used for further lookups
+ :type dbman: alot.db.DBManager
+ :param msg: the wrapped message
+ :type msg: notmuch.database.Message
+ :param thread: this messages thread
+ :type thread: alot.db.thread
+ """
+ self._dbman = dbman
+ self._id = msg.get_message_id()
+ self._thread_id = msg.get_thread_id()
+ self._thread = thread
+ self._datetime = datetime.fromtimestamp(msg.get_date())
+ self._filename = msg.get_filename()
+ # TODO: change api to return unicode
+ self._from = msg.get_header('From').decode('utf-8')
+ self._email = None # will be read upon first use
+ self._attachments = None # will be read upon first use
+ self._tags = set(msg.get_tags())
+
+ def __str__(self):
+ """prettyprint the message"""
+ aname, aaddress = self.get_author()
+ if not aname:
+ aname = aaddress
+ #tags = ','.join(self.get_tags())
+ return "%s (%s)" % (aname, self.get_datestring())
+
+ def __hash__(self):
+ """Implement hash(), so we can use Message() sets"""
+ return hash(self._id)
+
+ def __cmp__(self, other):
+ """Implement cmp(), so we can compare Message()s"""
+ res = cmp(self.get_message_id(), other.get_message_id())
+ return res
+
+ def get_email(self):
+ """returns email.Message representing this message"""
+ if not self._email:
+ f_mail = open(self.get_filename())
+ self._email = email.message_from_file(f_mail)
+ f_mail.close()
+ return self._email
+
+ def get_date(self):
+ """returns date as datetime obj"""
+ return self._datetime
+
+ def get_filename(self):
+ """returns absolute path of messages location"""
+ return self._filename
+
+ def get_message_id(self):
+ """returns messages id (a string)"""
+ return self._id
+
+ def get_thread_id(self):
+ """returns id of messages thread (a string)"""
+ return self._thread_id
+
+ def get_message_parts(self):
+ """returns a list of all body parts of this message"""
+ out = []
+ for msg in self.get_email().walk():
+ if not msg.is_multipart():
+ out.append(msg)
+ return out
+
+ def get_tags(self):
+ """returns tags attached to this message as list of strings"""
+ return list(self._tags)
+
+ def get_thread(self):
+ """returns the thread this msg belongs to as alot.db.Thread object"""
+ if not self._thread:
+ self._thread = self._dbman.get_thread(self._thread_id)
+ return self._thread
+
+ def get_replies(self):
+ """returns a list of replies to this msg"""
+ t = self.get_thread()
+ return t.get_replies_to(self)
+
+ def get_datestring(self, pretty=True):
+ """returns formated datestring in sup-style, eg: 'Yest.3pm'"""
+ return helper.pretty_datetime(self._datetime)
+
+ def get_author(self):
+ """returns realname and address pair of this messages author"""
+ return email.Utils.parseaddr(self._from)
+
+ def add_tags(self, tags):
+ """adds tags to message
+
+ :param tags: tags to add
+ :type tags: list of str
+ """
+ self._dbman.tag('id:' + self._id, tags)
+ self._tags = self._tags.union(tags)
+
+ def remove_tags(self, tags):
+ """remove tags from message
+
+ :param tags: tags to remove
+ :type tags: list of str
+ """
+ self._dbman.untag('id:' + self._id, tags)
+ self._tags = self._tags.difference(tags)
+
+ def get_attachments(self):
+ if not self._attachments:
+ self._attachments = []
+ for part in self.get_message_parts():
+ if part.get_content_maintype() != 'text':
+ self._attachments.append(Attachment(part))
+ return self._attachments
+
+ def accumulate_body(self):
+ return extract_body(self.get_email())
+
+
+def extract_body(mail):
+ bodytxt = ''
+ for part in mail.walk():
+ ctype = part.get_content_type()
+ enc = part.get_content_charset()
+ raw_payload = part.get_payload(decode=True)
+ if part.get_content_maintype() == 'text':
+ if enc:
+ raw_payload = unicode(raw_payload, enc)
+ else:
+ raw_payload = unicode(raw_payload, errors='replace')
+ if ctype == 'text/plain':
+ bodytxt += raw_payload
+ else:
+ #get mime handler
+ handler = get_mime_handler(ctype, key='view',
+ interactive=False)
+ if handler:
+ #open tempfile. Not all handlers accept stuff from stdin
+ tmpfile = tempfile.NamedTemporaryFile(delete=False,
+ suffix='.html')
+ #write payload to tmpfile
+ if part.get_content_maintype() == 'text':
+ tmpfile.write(raw_payload.encode('utf8'))
+ else:
+ tmpfile.write(raw_payload)
+ #create and call external command
+ cmd = handler % tmpfile.name
+ rendered_payload = helper.cmd_output(cmd)
+ #remove tempfile
+ tmpfile.close()
+ os.unlink(tmpfile.name)
+ if rendered_payload: # handler had output
+ bodytxt += unicode(rendered_payload.strip(),
+ encoding='utf8', errors='replace')
+ elif part.get_content_maintype() == 'text':
+ bodytxt += raw_payload
+ # else drop
+ return bodytxt
+
+class Attachment:
+ """represents a single mail attachment"""
+
+ def __init__(self, emailpart):
+ """
+ :param emailpart: a non-multipart email that is the attachment
+ :type emailpart: email.message.Message
+ """
+ self.part = emailpart
+
+ def __str__(self):
+ return '%s:%s (%s)' % (self.get_content_type(),
+ self.get_filename(),
+ self.get_size())
+
+ def get_filename(self):
+ """return the filename, extracted from content-disposition header"""
+ return self.part.get_filename()
+
+ def get_content_type(self):
+ """mime type of the attachment"""
+ return self.part.get_content_type()
+
+ def get_size(self):
+ """returns attachments size as human-readable string"""
+ size_in_kbyte = len(self.part.get_payload()) / 1024
+ if size_in_kbyte > 1024:
+ return "%.1fM" % (size_in_kbyte / 1024.0)
+ else:
+ return "%dK" % size_in_kbyte
+
+ def save(self, path):
+ """save the attachment to disk. Uses self.get_filename
+ in case path is a directory"""
+ if os.path.isdir(path):
+ path = os.path.join(path, self.get_filename())
+ FILE = open(path, "w")
+ FILE.write(self.part.get_payload(decode=True))
+ FILE.close()
diff --git a/alot/settings.py b/alot/settings.py
index 48485503..5b288069 100644
--- a/alot/settings.py
+++ b/alot/settings.py
@@ -29,7 +29,6 @@ DEFAULTS = {
'general': {
'colourmode': '16',
'editor_cmd': "/usr/bin/vim -f -c 'set filetype=mail' +",
- 'sendmail_cmd': 'msmtp --account=gmail -t',
'terminal_cmd': 'urxvt -T notmuch -e',
'spawn_editor': 'False',
'displayed_headers': 'From,To,Cc,Bcc,Subject',
@@ -39,6 +38,7 @@ DEFAULTS = {
'show_notificationbar': 'False',
'show_statusbar': 'True',
'flush_retry_timeout': '5',
+ 'hooksfile': '~/.alot.py',
},
'normal-theme': {
'bufferlist_focus_bg': 'dark gray',
@@ -212,43 +212,63 @@ class CustomConfigParser(SafeConfigParser):
value = self.get(section, option, **kwargs)
return [s.strip() for s in value.split(',')]
-
-config = CustomConfigParser(DEFAULTS)
-mailcaps = mailcap.getcaps()
-
-
-def setup(configfilename):
- config.read(os.path.expanduser(configfilename))
- if config.has_option('general', 'hooksfile'):
- hf = os.path.expanduser(config.get('general', 'hooksfile'))
- if hf is not None:
+ def read(self, *args):
+ SafeConfigParser.read(self, *args)
+ if self.has_option('general', 'hooksfile'):
+ hf = os.path.expanduser(config.get('general', 'hooksfile'))
+ if hf is not None:
+ try:
+ config.hooks = imp.load_source('hooks', hf)
+ except:
+ pass
+
+ def get_palette(self):
+ p = list()
+ for attr in DEFAULTS['mono-theme'].keys():
+ p.append((
+ attr,
+ self.get('normal-theme', attr + '_fg'),
+ self.get('normal-theme', attr + '_bg'),
+ self.get('mono-theme', attr),
+ self.get('highcolour-theme', attr + '_fg'),
+ self.get('highcolour-theme', attr + '_bg'),
+ ))
+ return p
+
+
+class HookManager:
+ def setup(self, hooksfile):
+ hf = os.path.expanduser(hooksfile)
+ if os.path.isfile(hf):
try:
- config.hooks = imp.load_source('hooks', hf)
+ self.module = imp.load_source('hooks', hf)
except:
- pass
+ self.module = None
+ else:
+ self.module = {}
+
+ def get(self, key):
+ if self.module:
+ if key in self.module.__dict__:
+ return self.module.__dict__[key]
+ def f(*args, **kwargs):
+ msg = 'called undefined hook: %s with arguments'
+ logging.debug(msg % key)
+ return f
-def get_palette():
- p = list()
- for attr in DEFAULTS['mono-theme'].keys():
- p.append((
- attr,
- config.get('normal-theme', attr + '_fg'),
- config.get('normal-theme', attr + '_bg'),
- config.get('mono-theme', attr),
- config.get('highcolour-theme', attr + '_fg'),
- config.get('highcolour-theme', attr + '_bg'),
- ))
- return p
+ def call(self, hookname, *args, **kwargs):
+ hook = self.get_hook(hookname)
+ try:
+ hook(*args, **kwargs)
+ except:
+ msg = 'exception occured while calling hook: %s with arguments %s, %s'
+ logging.exception(msg % hookname, args, kwargs)
-def get_hook(hookname):
- h = None
- if config.hooks:
- if config.hooks.__dict__:
- if hookname in config.hooks.__dict__:
- h = config.hooks.__dict__[hookname]
- return h
+config = CustomConfigParser(DEFAULTS)
+hooks = HookManager()
+mailcaps = mailcap.getcaps()
def get_mime_handler(mime_type, key, interactive=True):
@@ -266,47 +286,6 @@ def get_mime_handler(mime_type, key, interactive=True):
else:
return None
-
-def get_accounts():
- allowed = ['realname',
- 'address',
- 'gpg_key',
- 'signature',
- 'sender_type',
- 'sendmail_command',
- 'sent_mailbox']
- manditory = ['realname', 'address']
- sections = config.sections()
- accountsections = filter(lambda s: s.startswith('account '), sections)
- accounts = []
- for s in accountsections:
- options = filter(lambda x: x in allowed, config.options(s))
- args = {}
- for o in options:
- args[o] = config.get(s, o)
- if o in manditory:
- manditory.remove(o)
- if not manditory:
- logging.info(args)
- accounts.append(Account(**args))
- else:
- pass
- # log info
- return accounts
-
-
-def get_account_by_address(address):
- accounts = get_accounts()
- matched = [a for a in accounts if a.address == address]
- if len(matched) == 1:
- return matched.pop()
- else:
- return None
-
-
-def get_account_addresses():
- return [a.address for a in get_accounts()]
-
# maps mode to keybingins: for each one,
# a key is mapped to a pair cmdline, helpstring.
MAPPING = {
diff --git a/alot/ui.py b/alot/ui.py
index 8bd198ad..4cd35f76 100644
--- a/alot/ui.py
+++ b/alot/ui.py
@@ -21,7 +21,6 @@ import os
from urwid.command_map import command_map
from settings import config
-from settings import get_palette
from settings import get_mapping
from buffer import BufferListBuffer
from commandfactory import commandfactory
@@ -34,18 +33,18 @@ class UI:
buffers = []
current_buffer = None
- def __init__(self, db, log, accounts, initialquery, colourmode):
- self.dbman = db
+ def __init__(self, dbman, log, accountman, initialquery, colourmode):
+ self.dbman = dbman
self.dbman.ui = self # register ui with dbman
self.logger = log
- self.accounts = accounts
+ self.accountman = accountman
if not colourmode:
colourmode = config.getint('general', 'colourmode')
self.logger.info('setup gui in %d colours' % colourmode)
self.mainframe = urwid.Frame(urwid.SolidFill(' '))
self.mainloop = urwid.MainLoop(self.mainframe,
- get_palette(),
+ config.get_palette(),
handle_mouse=False,
unhandled_input=self.keypress)
self.mainloop.screen.set_terminal_properties(colors=colourmode)
@@ -111,7 +110,9 @@ class UI:
mode = self.current_buffer.typename
cmdline = self.prompt(prefix=':',
text=startstring,
- completer=CommandLineCompleter(self.dbman, mode))
+ completer=CommandLineCompleter(self.dbman,
+ self.accountman,
+ mode))
if cmdline:
cmd = interpret_commandline(cmdline, mode)
if cmd:
diff --git a/alot/widgets.py b/alot/widgets.py
index da35d6f3..e23352ac 100644
--- a/alot/widgets.py
+++ b/alot/widgets.py
@@ -23,7 +23,7 @@ from urwid.command_map import command_map
from settings import config
from helper import shorten
from helper import pretty_datetime
-from helper import cmd_output
+import message
class ThreadlineWidget(urwid.AttrMap):
@@ -264,7 +264,7 @@ class MessageWidget(urwid.WidgetWrap):
def _get_body_widget(self):
"""creates/returns the widget that displays the mail body"""
if not self.bodyw:
- cols = [MessageBodyWidget(self.message)]
+ cols = [MessageBodyWidget(self.message.get_email())]
bc = list()
if self.depth:
cols.insert(0, self._get_spacer(self.bars_at[1:]))
@@ -444,7 +444,7 @@ class MessageBodyWidget(urwid.AttrMap):
"""displays printable parts of an email"""
def __init__(self, msg):
- bodytxt = msg.accumulate_body()
+ bodytxt = message.extract_body(msg)
urwid.AttrMap.__init__(self, urwid.Text(bodytxt), 'message_body')
def selectable(self):