Create global instance of command-line options
This eases testing of option values inside the code. This instance is implemented as the read-only copy of the obtained 'options' object, so callers won't be able to modify its contents. Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
This commit is contained in:
parent
5c842c01bd
commit
f4140cbbed
@ -60,3 +60,30 @@ An :class:`accounts.Account` connects two email repositories that are to be sync
|
||||
This execption inherits directly from :exc:`Exception` and is raised
|
||||
on errors during the offlineimap execution. It has an attribute
|
||||
`severity` that denotes the severity level of the error.
|
||||
|
||||
|
||||
:mod:`offlineimap.globals` -- module with global variables
|
||||
==========================================================
|
||||
|
||||
.. module:: offlineimap.globals
|
||||
|
||||
Module :mod:`offlineimap.globals` provides the read-only storage
|
||||
for the global variables.
|
||||
|
||||
All exported module attributes can be set manually, but this practice
|
||||
is highly discouraged and shouldn't be used.
|
||||
However, attributes of all stored variables can only be read, write
|
||||
access to them is denied.
|
||||
|
||||
Currently, we have only :attr:`options` attribute that holds
|
||||
command-line options as returned by OptionParser.
|
||||
The value of :attr:`options` must be set by :func:`set_options`
|
||||
prior to its first use.
|
||||
|
||||
.. automodule:: offlineimap.globals
|
||||
:members:
|
||||
|
||||
.. data:: options
|
||||
|
||||
You can access the values of stored options using the usual
|
||||
syntax, offlineimap.globals.options.<option-name>
|
||||
|
@ -15,6 +15,7 @@
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from offlineimap import mbnames, CustomConfig, OfflineImapError
|
||||
from offlineimap import globals
|
||||
from offlineimap.repository import Repository
|
||||
from offlineimap.ui import getglobalui
|
||||
from offlineimap.threadutil import InstanceLimitedThread
|
||||
@ -321,7 +322,7 @@ class SyncableAccount(Account):
|
||||
self.ui.debug('', "Not syncing filtered folder '%s'"
|
||||
"[%s]" % (localfolder, localfolder.repository))
|
||||
continue # Ignore filtered folder
|
||||
if self.config.get('general', 'single-thread') == 'False':
|
||||
if not globals.options.singlethreading:
|
||||
thread = InstanceLimitedThread(\
|
||||
instancename = 'FOLDER_' + self.remoterepos.getname(),
|
||||
target = syncfolder,
|
||||
|
@ -16,6 +16,7 @@
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from offlineimap import threadutil
|
||||
from offlineimap import globals
|
||||
from offlineimap.ui import getglobalui
|
||||
from offlineimap.error import OfflineImapError
|
||||
import offlineimap.accounts
|
||||
@ -403,7 +404,7 @@ class BaseFolder(object):
|
||||
break
|
||||
self.ui.copyingmessage(uid, num+1, num_to_copy, self, dstfolder)
|
||||
# exceptions are caught in copymessageto()
|
||||
if self.suggeststhreads() and self.config.get('general', 'single-thread') == 'False':
|
||||
if self.suggeststhreads() and not globals.options.singlethreading:
|
||||
self.waitforthread()
|
||||
thread = threadutil.InstanceLimitedThread(\
|
||||
self.getcopyinstancelimit(),
|
||||
|
12
offlineimap/globals.py
Normal file
12
offlineimap/globals.py
Normal file
@ -0,0 +1,12 @@
|
||||
# Copyright 2013 Eygene A. Ryabinkin.
|
||||
#
|
||||
# Module that holds various global objects.
|
||||
|
||||
from offlineimap.utils import const
|
||||
|
||||
# Holds command-line options for OfflineIMAP.
|
||||
options = const.ConstProxy()
|
||||
|
||||
def set_options (source):
|
||||
""" Sets the source for options variable """
|
||||
options.set_source (source)
|
@ -25,6 +25,7 @@ import logging
|
||||
from optparse import OptionParser
|
||||
import offlineimap
|
||||
from offlineimap import accounts, threadutil, syncmaster
|
||||
from offlineimap import globals
|
||||
from offlineimap.error import OfflineImapError
|
||||
from offlineimap.ui import UI_LIST, setglobalui, getglobalui
|
||||
from offlineimap.CustomConfig import CustomConfigParser
|
||||
@ -161,6 +162,7 @@ class OfflineImap:
|
||||
", ".join(UI_LIST.keys()))
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
globals.set_options (options)
|
||||
|
||||
#read in configuration file
|
||||
configfilename = os.path.expanduser(options.configfile)
|
||||
@ -251,9 +253,6 @@ class OfflineImap:
|
||||
if type.lower() == 'imap':
|
||||
imaplib.Debug = 5
|
||||
|
||||
# XXX: can we avoid introducing fake configuration item?
|
||||
config.set_if_not_exists('general', 'single-thread', 'True' if options.singlethreading else 'False')
|
||||
|
||||
if options.runonce:
|
||||
# FIXME: maybe need a better
|
||||
for section in accounts.getaccountlist(config):
|
||||
|
40
offlineimap/utils/const.py
Normal file
40
offlineimap/utils/const.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright 2013 Eygene A. Ryabinkin.
|
||||
#
|
||||
# Collection of classes that implement const-like behaviour
|
||||
# for various objects.
|
||||
|
||||
import copy
|
||||
|
||||
class ConstProxy (object):
|
||||
"""
|
||||
Implements read-only access to a given object
|
||||
that can be attached to each instance only once.
|
||||
|
||||
"""
|
||||
|
||||
def __init__ (self):
|
||||
self.__dict__['__source'] = None
|
||||
|
||||
|
||||
def __getattr__ (self, name):
|
||||
src = self.__dict__['__source']
|
||||
if src == None:
|
||||
raise ValueError ("using non-initialized ConstProxy() object")
|
||||
return copy.deepcopy (getattr (src, name))
|
||||
|
||||
|
||||
def __setattr__ (self, name, value):
|
||||
raise AttributeError ("tried to set '%s' to '%s' for constant object" % \
|
||||
(name, value))
|
||||
|
||||
|
||||
def __delattr__ (self, name):
|
||||
raise RuntimeError ("tried to delete field '%s' from constant object" % \
|
||||
(name))
|
||||
|
||||
|
||||
def set_source (self, source):
|
||||
""" Sets source object for this instance. """
|
||||
if (self.__dict__['__source'] != None):
|
||||
raise ValueError ("source object is already set")
|
||||
self.__dict__['__source'] = source
|
51
test/tests/test_00_globals.py
Executable file
51
test/tests/test_00_globals.py
Executable file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2013 Eygene A. Ryabinkin
|
||||
|
||||
from offlineimap import globals
|
||||
import unittest
|
||||
|
||||
class Opt:
|
||||
def __init__(self):
|
||||
self.one = "baz"
|
||||
self.two = 42
|
||||
self.three = True
|
||||
|
||||
|
||||
class TestOfflineimapGlobals(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(klass):
|
||||
klass.o = Opt()
|
||||
globals.set_options (klass.o)
|
||||
|
||||
def test_initial_state(self):
|
||||
for k in self.o.__dict__.keys():
|
||||
self.assertTrue(getattr(self.o, k) ==
|
||||
getattr(globals.options, k))
|
||||
|
||||
def test_object_changes(self):
|
||||
self.o.one = "one"
|
||||
self.o.two = 119
|
||||
self.o.three = False
|
||||
return self.test_initial_state()
|
||||
|
||||
def test_modification(self):
|
||||
with self.assertRaises(AttributeError):
|
||||
globals.options.two = True
|
||||
|
||||
def test_deletion(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
del globals.options.three
|
||||
|
||||
def test_nonexistent_key(self):
|
||||
with self.assertRaises(AttributeError):
|
||||
a = globals.options.nosuchoption
|
||||
|
||||
def test_double_init(self):
|
||||
with self.assertRaises(ValueError):
|
||||
globals.set_options (True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestOfflineimapGlobals)
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
Loading…
Reference in New Issue
Block a user