diff options
author | patrick <p.totzke@ed.ac.uk> | 2011-07-26 14:29:53 +0100 |
---|---|---|
committer | patrick <p.totzke@ed.ac.uk> | 2011-07-26 14:29:53 +0100 |
commit | 375fe29e1859c13d4c61d04bc171678bbb4d96f0 (patch) | |
tree | d3e1fe94f1e651197333bccb1410d9033b09bcaf | |
parent | d64475a92e472865d50f5ac6b7c0c39c6654ff5c (diff) | |
parent | b4f761bd7afbd0f4302ed2c5bdedfe29a82383d0 (diff) |
Merge branch 'restructure'
-rw-r--r-- | alot/account.py | 44 | ||||
-rw-r--r-- | alot/commandfactory.py | 6 | ||||
-rw-r--r-- | alot/commands.py | 25 | ||||
-rw-r--r-- | alot/completion.py | 8 | ||||
-rw-r--r-- | alot/db.py | 227 | ||||
-rwxr-xr-x | alot/init.py | 11 | ||||
-rw-r--r-- | alot/message.py | 230 | ||||
-rw-r--r-- | alot/settings.py | 125 | ||||
-rw-r--r-- | alot/ui.py | 13 | ||||
-rw-r--r-- | alot/widgets.py | 6 |
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) @@ -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 = { @@ -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): |