From 325dd833ba4f75ec13359afeb0dcc3077d880595 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Mon, 6 Dec 2010 13:36:54 +0100 Subject: [PATCH] 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 Signed-off-by: Nicolas Sebrecht --- bin/offlineimap | 6 +- offlineimap.py | 6 +- offlineimap/__init__.py | 2 + offlineimap/init.py | 383 ++++++++++++++++++++-------------------- 4 files changed, 203 insertions(+), 194 deletions(-) diff --git a/bin/offlineimap b/bin/offlineimap index 03700ca..420ddb0 100755 --- a/bin/offlineimap +++ b/bin/offlineimap @@ -17,5 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from offlineimap import init -init.startup('6.2.0') +from offlineimap import OfflineImap + +offlineimap = OfflineImap() +offlineimap.startup('6.2.0') diff --git a/offlineimap.py b/offlineimap.py index e8684e4..566bd36 100755 --- a/offlineimap.py +++ b/offlineimap.py @@ -17,5 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -from offlineimap import init -init.startup('6.2.0') +from offlineimap import OfflineImap + +offlineimap = OfflineImap() +offlineimap.startup('6.2.0') diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py index b19e419..c36246c 100644 --- a/offlineimap/__init__.py +++ b/offlineimap/__init__.py @@ -1,3 +1,5 @@ +from offlineimap.init import OfflineImap + __all__ = ['ui', 'folder', 'repository', 'mbnames', 'threadutil', 'init'] diff --git a/offlineimap/init.py b/offlineimap/init.py index 4d015ec..f930c0f 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -36,198 +36,201 @@ except: lockfd = None -def lock(config, ui): - 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) +class OfflineImap: -def startup(versionno): - 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 = {} - options['-k'] = [] - if '--help' in sys.argv[1:]: - sys.stdout.write(version.getcmdhelp() + "\n") - sys.exit(0) - - for optlist in getopt(sys.argv[1:], 'P:1oqa:c:d:l:u:hk:f:')[0]: - if optlist[0] == '-k': - options[optlist[0]].append(optlist[1]) - else: - options[optlist[0]] = optlist[1] - - if options.has_key('-h'): - sys.stdout.write(version.getcmdhelp()) - sys.stdout.write("\n") - 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 = CustomConfigParser() - if not os.path.exists(configfilename): - sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename) - sys.exit(1) - - config.read(configfilename) - - # override config values with option '-k' - 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')) - UIBase.setglobalui(ui) - - if options.has_key('-l'): - 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) - - 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: + def lock(self, config, ui): + 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(self, versionno): + 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 = {} + options['-k'] = [] + if '--help' in sys.argv[1:]: + sys.stdout.write(version.getcmdhelp() + "\n") + sys.exit(0) + + for optlist in getopt(sys.argv[1:], 'P:1oqa:c:d:l:u:hk:f:')[0]: + if optlist[0] == '-k': + options[optlist[0]].append(optlist[1]) + else: + options[optlist[0]] = optlist[1] + + if options.has_key('-h'): + sys.stdout.write(version.getcmdhelp()) + sys.stdout.write("\n") + 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 = CustomConfigParser() + if not os.path.exists(configfilename): + sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename) + sys.exit(1) + + config.read(configfilename) + + # override config values with option '-k' + 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')) + UIBase.setglobalui(ui) + 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: - socket.setdefaulttimeout(socktimeout) - - activeaccounts = config.get("general", "accounts") - if options.has_key('-a'): - activeaccounts = options['-a'] - activeaccounts = activeaccounts.replace(" ", "") - activeaccounts = activeaccounts.split(",") - allaccounts = accounts.AccountHashGenerator(config) - - syncaccounts = [] - for account in activeaccounts: - if account not in allaccounts: - if len(allaccounts) == 0: - 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 - 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. + + def sigterm_handler(self, 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'): + sys.stderr = ui.logfile + + socktimeout = config.getdefaultint("general", "socktimeout", 0) + if socktimeout > 0: + socket.setdefaulttimeout(socktimeout) + + activeaccounts = config.get("general", "accounts") + if options.has_key('-a'): + activeaccounts = options['-a'] + activeaccounts = activeaccounts.replace(" ", "") + activeaccounts = activeaccounts.split(",") + allaccounts = accounts.AccountHashGenerator(config) + + syncaccounts = [] + for account in activeaccounts: + if account not in allaccounts: + if len(allaccounts) == 0: + 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 + 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.