Make a main class OfflineImap that is being called

Rather than calling a function in a module, invoke offlineimap by
calling an OfflineImap object.

This removes code lying outside of objects; I prefer to keep code
within an object and provides us with a nicer Object encapsulation.

It will also ease the testing of Object functionality in unittests
when they are introduced.

Previously we would import and start Offlineimap like this:

from offlineimap import init
init.startup('6.2.0')

now we do:
from offlineimap import OfflineImap

offlineimap = OfflineImap()
offlineimap.startup('6.2.0')

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Sebastian Spaeth 2010-12-06 13:36:54 +01:00 committed by Nicolas Sebrecht
parent 35dd236155
commit 325dd833ba
4 changed files with 203 additions and 194 deletions

View File

@ -17,5 +17,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from offlineimap import init from offlineimap import OfflineImap
init.startup('6.2.0')
offlineimap = OfflineImap()
offlineimap.startup('6.2.0')

View File

@ -17,5 +17,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from offlineimap import init from offlineimap import OfflineImap
init.startup('6.2.0')
offlineimap = OfflineImap()
offlineimap.startup('6.2.0')

View File

@ -1,3 +1,5 @@
from offlineimap.init import OfflineImap
__all__ = ['ui', 'folder', 'repository', 'mbnames', 'threadutil', 'init'] __all__ = ['ui', 'folder', 'repository', 'mbnames', 'threadutil', 'init']

View File

@ -36,198 +36,201 @@ except:
lockfd = None lockfd = None
def lock(config, ui): class OfflineImap:
global lockfd, hasfcntl
if not hasfcntl:
return
lockfd = open(config.getmetadatadir() + "/lock", "w")
try:
fcntl.flock(lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
ui.locked()
ui.terminate(1)
def startup(versionno): def lock(self, config, ui):
assert versionno == version.versionstr, "Revision of main program (%s) does not match that of library (%s). Please double-check your PYTHONPATH and installation locations." % (versionno, version.versionstr) global lockfd, hasfcntl
options = {} if not hasfcntl:
options['-k'] = [] return
if '--help' in sys.argv[1:]: lockfd = open(config.getmetadatadir() + "/lock", "w")
sys.stdout.write(version.getcmdhelp() + "\n") try:
sys.exit(0) fcntl.flock(lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
for optlist in getopt(sys.argv[1:], 'P:1oqa:c:d:l:u:hk:f:')[0]: ui.locked()
if optlist[0] == '-k': ui.terminate(1)
options[optlist[0]].append(optlist[1])
else: def startup(self, versionno):
options[optlist[0]] = optlist[1] assert versionno == version.versionstr, "Revision of main program (%s) does not match that of library (%s). Please double-check your PYTHONPATH and installation locations." % (versionno, version.versionstr)
options = {}
if options.has_key('-h'): options['-k'] = []
sys.stdout.write(version.getcmdhelp()) if '--help' in sys.argv[1:]:
sys.stdout.write("\n") sys.stdout.write(version.getcmdhelp() + "\n")
sys.exit(0) sys.exit(0)
configfilename = os.path.expanduser("~/.offlineimaprc")
if options.has_key('-c'): for optlist in getopt(sys.argv[1:], 'P:1oqa:c:d:l:u:hk:f:')[0]:
configfilename = options['-c'] if optlist[0] == '-k':
if options.has_key('-P'): options[optlist[0]].append(optlist[1])
if not options.has_key('-1'): else:
sys.stderr.write("FATAL: profile mode REQUIRES -1\n") options[optlist[0]] = optlist[1]
sys.exit(100)
profiledir = options['-P'] if options.has_key('-h'):
os.mkdir(profiledir) sys.stdout.write(version.getcmdhelp())
threadutil.setprofiledir(profiledir) sys.stdout.write("\n")
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n") sys.exit(0)
configfilename = os.path.expanduser("~/.offlineimaprc")
config = CustomConfigParser() if options.has_key('-c'):
if not os.path.exists(configfilename): configfilename = options['-c']
sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename) if options.has_key('-P'):
sys.exit(1) if not options.has_key('-1'):
sys.stderr.write("FATAL: profile mode REQUIRES -1\n")
config.read(configfilename) sys.exit(100)
profiledir = options['-P']
# override config values with option '-k' os.mkdir(profiledir)
for option in options['-k']: threadutil.setprofiledir(profiledir)
(key, value) = option.split('=', 1) sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
if ':' in key:
(secname, key) = key.split(':', 1) config = CustomConfigParser()
section = secname.replace("_", " ") if not os.path.exists(configfilename):
else: sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename)
section = "general" sys.exit(1)
config.set(section, key, value)
config.read(configfilename)
ui = offlineimap.ui.detector.findUI(config, options.get('-u'))
UIBase.setglobalui(ui) # override config values with option '-k'
for option in options['-k']:
if options.has_key('-l'): (key, value) = option.split('=', 1)
ui.setlogfd(open(options['-l'], 'wt')) if ':' in key:
(secname, key) = key.split(':', 1)
ui.init_banner() section = secname.replace("_", " ")
else:
if options.has_key('-d'): section = "general"
for debugtype in options['-d'].split(','): config.set(section, key, value)
ui.add_debug(debugtype.strip())
if debugtype == 'imap': ui = offlineimap.ui.detector.findUI(config, options.get('-u'))
imaplib.Debug = 5 UIBase.setglobalui(ui)
if debugtype == 'thread':
threading._VERBOSE = 1
if options.has_key('-o'):
# FIXME: maybe need a better
for section in accounts.getaccountlist(config):
config.remove_option('Account ' + section, "autorefresh")
if options.has_key('-q'):
for section in accounts.getaccountlist(config):
config.set('Account ' + section, "quick", '-1')
if options.has_key('-f'):
foldernames = options['-f'].replace(" ", "").split(",")
folderfilter = "lambda f: f in %s" % foldernames
folderincludes = "[]"
for accountname in accounts.getaccountlist(config):
account_section = 'Account ' + accountname
remote_repo_section = 'Repository ' + \
config.get(account_section, 'remoterepository')
local_repo_section = 'Repository ' + \
config.get(account_section, 'localrepository')
for section in [remote_repo_section, local_repo_section]:
config.set(section, "folderfilter", folderfilter)
config.set(section, "folderincludes", folderincludes)
lock(config, ui)
def sigterm_handler(signum, frame):
# die immediately
ui.terminate(errormsg="terminating...")
signal.signal(signal.SIGTERM,sigterm_handler)
try:
pidfd = open(config.getmetadatadir() + "/pid", "w")
pidfd.write(str(os.getpid()) + "\n")
pidfd.close()
except:
pass
try:
if options.has_key('-l'): if options.has_key('-l'):
sys.stderr = ui.logfile ui.setlogfd(open(options['-l'], 'wt'))
ui.init_banner()
if options.has_key('-d'):
for debugtype in options['-d'].split(','):
ui.add_debug(debugtype.strip())
if debugtype == 'imap':
imaplib.Debug = 5
if debugtype == 'thread':
threading._VERBOSE = 1
if options.has_key('-o'):
# FIXME: maybe need a better
for section in accounts.getaccountlist(config):
config.remove_option('Account ' + section, "autorefresh")
if options.has_key('-q'):
for section in accounts.getaccountlist(config):
config.set('Account ' + section, "quick", '-1')
if options.has_key('-f'):
foldernames = options['-f'].replace(" ", "").split(",")
folderfilter = "lambda f: f in %s" % foldernames
folderincludes = "[]"
for accountname in accounts.getaccountlist(config):
account_section = 'Account ' + accountname
remote_repo_section = 'Repository ' + \
config.get(account_section, 'remoterepository')
local_repo_section = 'Repository ' + \
config.get(account_section, 'localrepository')
for section in [remote_repo_section, local_repo_section]:
config.set(section, "folderfilter", folderfilter)
config.set(section, "folderincludes", folderincludes)
self.lock(config, ui)
socktimeout = config.getdefaultint("general", "socktimeout", 0)
if socktimeout > 0: def sigterm_handler(self, signum, frame):
socket.setdefaulttimeout(socktimeout) # die immediately
ui.terminate(errormsg="terminating...")
activeaccounts = config.get("general", "accounts") signal.signal(signal.SIGTERM,sigterm_handler)
if options.has_key('-a'):
activeaccounts = options['-a'] try:
activeaccounts = activeaccounts.replace(" ", "") pidfd = open(config.getmetadatadir() + "/pid", "w")
activeaccounts = activeaccounts.split(",") pidfd.write(str(os.getpid()) + "\n")
allaccounts = accounts.AccountHashGenerator(config) pidfd.close()
except:
syncaccounts = [] pass
for account in activeaccounts:
if account not in allaccounts: try:
if len(allaccounts) == 0: if options.has_key('-l'):
errormsg = 'The account "%s" does not exist because no accounts are defined!'%account sys.stderr = ui.logfile
else:
errormsg = 'The account "%s" does not exist. Valid accounts are:'%account socktimeout = config.getdefaultint("general", "socktimeout", 0)
for name in allaccounts.keys(): if socktimeout > 0:
errormsg += '\n%s'%name socket.setdefaulttimeout(socktimeout)
ui.terminate(1, errortitle = 'Unknown Account "%s"'%account, errormsg = errormsg)
if account not in syncaccounts: activeaccounts = config.get("general", "accounts")
syncaccounts.append(account) if options.has_key('-a'):
activeaccounts = options['-a']
server = None activeaccounts = activeaccounts.replace(" ", "")
remoterepos = None activeaccounts = activeaccounts.split(",")
localrepos = None allaccounts = accounts.AccountHashGenerator(config)
if options.has_key('-1'): syncaccounts = []
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1) for account in activeaccounts:
else: if account not in allaccounts:
threadutil.initInstanceLimit("ACCOUNTLIMIT", if len(allaccounts) == 0:
config.getdefaultint("general", "maxsyncaccounts", 1)) errormsg = 'The account "%s" does not exist because no accounts are defined!'%account
else:
for reposname in config.getsectionlist('Repository'): errormsg = 'The account "%s" does not exist. Valid accounts are:'%account
for instancename in ["FOLDER_" + reposname, for name in allaccounts.keys():
"MSGCOPY_" + reposname]: errormsg += '\n%s'%name
if options.has_key('-1'): ui.terminate(1, errortitle = 'Unknown Account "%s"'%account, errormsg = errormsg)
threadutil.initInstanceLimit(instancename, 1) if account not in syncaccounts:
else: syncaccounts.append(account)
threadutil.initInstanceLimit(instancename,
config.getdefaultint('Repository ' + reposname, "maxconnections", 1)) server = None
siglisteners = [] remoterepos = None
def sig_handler(signum, frame): localrepos = None
if signum == signal.SIGUSR1:
# tell each account to do a full sync asap if options.has_key('-1'):
signum = (1,) threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
elif signum == signal.SIGHUP: else:
# tell each account to die asap threadutil.initInstanceLimit("ACCOUNTLIMIT",
signum = (2,) config.getdefaultint("general", "maxsyncaccounts", 1))
elif signum == signal.SIGUSR2:
# tell each account to do a full sync asap, then die for reposname in config.getsectionlist('Repository'):
signum = (1, 2) for instancename in ["FOLDER_" + reposname,
# one listener per account thread (up to maxsyncaccounts) "MSGCOPY_" + reposname]:
for listener in siglisteners: if options.has_key('-1'):
for sig in signum: threadutil.initInstanceLimit(instancename, 1)
listener.put_nowait(sig) else:
signal.signal(signal.SIGHUP,sig_handler) threadutil.initInstanceLimit(instancename,
signal.signal(signal.SIGUSR1,sig_handler) config.getdefaultint('Repository ' + reposname, "maxconnections", 1))
signal.signal(signal.SIGUSR2,sig_handler) siglisteners = []
def sig_handler(signum, frame):
threadutil.initexitnotify() if signum == signal.SIGUSR1:
t = ExitNotifyThread(target=syncmaster.syncitall, # tell each account to do a full sync asap
name='Sync Runner', signum = (1,)
kwargs = {'accounts': syncaccounts, elif signum == signal.SIGHUP:
'config': config, # tell each account to die asap
'siglisteners': siglisteners}) signum = (2,)
t.setDaemon(1) elif signum == signal.SIGUSR2:
t.start() # tell each account to do a full sync asap, then die
except: signum = (1, 2)
ui.mainException() # one listener per account thread (up to maxsyncaccounts)
for listener in siglisteners:
try: for sig in signum:
threadutil.exitnotifymonitorloop(threadutil.threadexited) listener.put_nowait(sig)
except SystemExit: signal.signal(signal.SIGHUP,sig_handler)
raise signal.signal(signal.SIGUSR1,sig_handler)
except: signal.signal(signal.SIGUSR2,sig_handler)
ui.mainException() # Also expected to terminate.
threadutil.initexitnotify()
t = ExitNotifyThread(target=syncmaster.syncitall,
name='Sync Runner',
kwargs = {'accounts': syncaccounts,
'config': config,
'siglisteners': siglisteners})
t.setDaemon(1)
t.start()
except:
ui.mainException()
try:
threadutil.exitnotifymonitorloop(threadutil.threadexited)
except SystemExit:
raise
except:
ui.mainException() # Also expected to terminate.