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