def test_layered_subsections(self): defaults = OrderedDict((('force', False), ('home', 'thisdata'), ('loglevel', 'INFO'))) cmdline = ['--mymodule-home=thatdata', '--mymodule-force'] cfg = LayeredConfig(Defaults(defaults), Commandline(cmdline), cascade=True) self.assertEqual(cfg.mymodule.force, True) self.assertEqual(cfg.mymodule.home, 'thatdata') self.assertEqual(cfg.mymodule.loglevel, 'INFO') # second test is more difficult: the lower-priority Defaults # source only contains a subsection, while the higher-priority # Commandline source contains no such subsection. Our # sub-LayeredConfig object will only have a Defaults source, # not a Commandline source (which will cause the # __getattribute__ lookup_resource to look in the Defaults # object in the sub-LayeredConfig object, unless we do # something smart. defaults = {'mymodule': defaults} cmdline = ['--home=thatdata', '--force'] o = Commandline(cmdline) o.subsection("mymodule").keys() cfg = LayeredConfig(Defaults(defaults), Commandline(cmdline), cascade=True) self.assertEqual(cfg.mymodule.force, True) self.assertEqual(cfg.mymodule.home, 'thatdata') self.assertEqual(cfg.mymodule.loglevel, 'INFO') self.assertEqual(['force', 'home', 'loglevel'], list(cfg.mymodule))
def test_commandline_implicit_typing(self): # The big test here is really the partially-configured # ArgumentParser (handles one positional argument but not the # optional --force) defaults = {'force': False} cmdline = ['command', '--force'] parser = argparse.ArgumentParser() parser.add_argument("positional") cmdlinesrc = Commandline(cmdline, parser=parser) cfg = LayeredConfig(Defaults(defaults), cmdlinesrc) self.assertEqual(cfg.force, True) # try again with explicit argument parser = argparse.ArgumentParser() parser.add_argument("positional") cmdlinesrc = Commandline(['command', '--force=True'], parser=parser) cfg = LayeredConfig(Defaults(defaults), cmdlinesrc) self.assertEqual(cfg.force, True) # once again without the optional typing source parser = argparse.ArgumentParser() parser.add_argument("positional") cmdlinesrc = Commandline(['command', '--force'], parser=parser) cfg = LayeredConfig(Defaults({}), cmdlinesrc) self.assertEqual(cfg.force, True)
class TestCommandline(unittest.TestCase, TestConfigSourceHelper): # Note: bool is "half-way" supported. Only value-less parameters # are typed as bool (eg "--force", not "--force=True") supported_types = (str, list, bool) simple_cmdline = [ '--home=mydata', '--processes=4', '--force', # note implicit boolean typing '--extra=foo', '--extra=bar', '--expires=2014-10-15', '--lastrun=2014-10-15 14:32:07' ] complex_cmdline = [ '--home=mydata', '--processes=4', '--force=True', '--extra=foo', '--extra=bar', '--mymodule-force=False', '--mymodule-extra=foo', '--mymodule-extra=baz', '--mymodule-expires=2014-10-15', '--mymodule-arbitrary-nesting-depth=works', '--extramodule-unique' ] def setUp(self): super(TestCommandline, self).setUp() # this means we lack typing information self.simple = Commandline(self.simple_cmdline) self.complex = Commandline(self.complex_cmdline) # Overrides of TestHelper.test_get, .test_typed and and due to # limitations of Commandline (carries almost no typeinfo) def test_get(self): self.assertEqual(self.simple.get("home"), "mydata") self.assertEqual(self.simple.get("processes"), "4") self.assertEqual(self.simple.get("force"), True) self.assertEqual(self.simple.get("extra"), ['foo', 'bar']) # note typed! self.assertEqual(self.simple.get("expires"), "2014-10-15") self.assertEqual(self.simple.get("lastrun"), "2014-10-15 14:32:07") def test_typed(self): for key in self.simple.keys(): # these should be typed as bool and list, respectively if key in ("force", "extra"): self.assertTrue(self.simple.typed(key)) else: self.assertFalse(self.simple.typed(key)) def test_config_subsections(self): # this case uses valued parameter for --force et al, which # cannot be reliably converted to bools using only intrinsic # information self.supported_types = (str, list) super(TestCommandline, self).test_config_subsections() def test_set(self): self.simple.set("home", "away from home") self.assertEqual(self.simple.get("home"), "away from home")
def test_modified_subsections(self): defaults = {'force': False, 'home': 'thisdata', 'loglevel': 'INFO'} cmdline = ['--mymodule-home=thatdata', '--mymodule-force'] cfg = LayeredConfig(Defaults(defaults), INIFile("complex.ini"), Commandline(cmdline), cascade=True) cfg.mymodule.expires = date(2014, 10, 24)
def test_set_novalue(self): # it should be possible to set values that are defined in any # of the configsources, even though only typing information # exists there. cfg = LayeredConfig(Defaults({'placeholder': int}), Commandline([])) cfg.placeholder = 42 # but it shouldn't be possible to set values that hasn't been # defined anywhere. with self.assertRaises(AttributeError): cfg.nonexistent = 43
def test_typed_commandline(self): cmdline = [ '--home=mydata', '--processes=4', '--force=True', '--extra=foo', '--extra=bar', '--implicitboolean', '--mymodule-force=False', '--mymodule-extra=foo', '--mymodule-extra=baz', '--mymodule-expires=2014-10-15', '--mymodule-arbitrary-nesting-depth=works', '--extramodule-unique' ] cfg = LayeredConfig(Defaults(self.types), Commandline(cmdline)) self._test_config_subsections(cfg) self.assertTrue(cfg.implicitboolean) self.assertIs(type(cfg.implicitboolean), bool)
def test_layered_subsections(self): defaults = OrderedDict( (('force', False), ('home', 'thisdata'), ('loglevel', 'INFO'))) cmdline = ['--mymodule-home=thatdata', '--mymodule-force'] cfg = LayeredConfig(Defaults(defaults), Commandline(cmdline), cascade=True) self.assertEqual(cfg.mymodule.force, True) self.assertEqual(cfg.mymodule.home, 'thatdata') self.assertEqual(cfg.mymodule.loglevel, 'INFO') # second test is more difficult: the lower-priority Defaults # source only contains a subsection, while the higher-priority # Commandline source contains no such subsection. Our # sub-LayeredConfig object will only have a Defaults source, # not a Commandline source (which will cause the # __getattribute__ lookup_resource to look in the Defaults # object in the sub-LayeredConfig object, unless we do # something smart. defaults = {'mymodule': defaults} cmdline = ['--home=thatdata', '--force'] o = Commandline(cmdline) o.subsection("mymodule").keys() cfg = LayeredConfig(Defaults(defaults), Commandline(cmdline), cascade=True) self.assertEqual(cfg.mymodule.force, True) self.assertEqual(cfg.mymodule.home, 'thatdata') self.assertEqual(cfg.mymodule.loglevel, 'INFO') self.assertEqual(['force', 'home', 'loglevel'], list(cfg.mymodule))
def test_typed_novalue(self): # this cmdline only sets some of the settings. The test is # that the rest should raise AttributeError (not return None, # as was the previous behaviour), and that __iter__ should not # include them. cmdline = ['--processes=4', '--force=False'] cfg = LayeredConfig(Defaults(self.types), Commandline(cmdline)) self.assertEqual(4, cfg.processes) self.assertIsInstance(cfg.processes, int) with self.assertRaises(AttributeError): cfg.home with self.assertRaises(AttributeError): cfg.extra self.assertEqual(set(['processes', 'force']), set(list(cfg)))
def setUp(self): super(TestCommandlineConfigured, self).setUp() simp = argparse.ArgumentParser(description="This is a simple program") simp.add_argument('--home', help="The home directory of the app") simp.add_argument('--processes', type=int, help="Number of simultaneous processes") simp.add_argument('--force', type=LayeredConfig.boolconvert, nargs='?', const=True) simp.add_argument('--extra', action='append') simp.add_argument('--expires', type=LayeredConfig.dateconvert) simp.add_argument('--lastrun', type=LayeredConfig.datetimeconvert) simp.add_argument('--unused') self.simple = Commandline(self.simple_cmdline, parser=simp) comp = argparse.ArgumentParser(description="This is a complex program") comp.add_argument('--home', help="The home directory of the app") comp.add_argument('--processes', type=int, help="Number of simultaneous processes") comp.add_argument('--force', type=LayeredConfig.boolconvert, nargs='?', const=True) comp.add_argument('--extra', action='append') comp.add_argument('--mymodule-force', type=LayeredConfig.boolconvert, nargs='?', const=True) comp.add_argument('--mymodule-extra', action='append') comp.add_argument('--mymodule-expires', type=LayeredConfig.dateconvert) comp.add_argument('--mymodule-arbitrary-nesting-depth') comp.add_argument('--extramodule-unique', nargs='?', const=True) self.complex = Commandline(self.complex_cmdline, parser=comp)
def test_layered(self): defaults = {'home': 'someplace'} cmdline = ['--home=anotherplace'] env = {'MYAPP_HOME': 'yourdata'} cfg = LayeredConfig(Defaults(defaults)) self.assertEqual(cfg.home, 'someplace') cfg = LayeredConfig(Defaults(defaults), INIFile("simple.ini")) self.assertEqual(cfg.home, 'mydata') cfg = LayeredConfig(Defaults(defaults), INIFile("simple.ini"), Environment(env, prefix="MYAPP_")) self.assertEqual(cfg.home, 'yourdata') cfg = LayeredConfig(Defaults(defaults), INIFile("simple.ini"), Environment(env, prefix="MYAPP_"), Commandline(cmdline)) self.assertEqual(cfg.home, 'anotherplace') self.assertEqual( ['home', 'processes', 'force', 'extra', 'expires', 'lastrun'], list(cfg))
def test_typed_commandline_cascade(self): # the test here is that __getattribute__ must determine that # subconfig.force is not typed in itself, and fetch type # information from the root of defaults defaults = {'force': True, 'lastdownload': datetime, 'mymodule': {}} cmdline = ['--mymodule-force=False'] cfg = LayeredConfig(Defaults(defaults), Commandline(cmdline), cascade=True) subconfig = getattr(cfg, 'mymodule') self.assertIs(type(subconfig.force), bool) self.assertEqual(subconfig.force, False) # test typed config values that have no actual value. Since # they have no value, they should raise AtttributeError with self.assertRaises(AttributeError): self.assertEqual(cfg.lastdownload, None) with self.assertRaises(AttributeError): self.assertEqual(subconfig.lastdownload, None)
def setUp(self): super(TestCommandline, self).setUp() # this means we lack typing information self.simple = Commandline(self.simple_cmdline) self.complex = Commandline(self.complex_cmdline)
def test_typed_override(self): # make sure this auto-typing isn't run for bools types = {'logfile': True} cmdline = ["--logfile=out.log"] cfg = LayeredConfig(Defaults(types), Commandline(cmdline)) self.assertEqual(cfg.logfile, "out.log")
class TestCommandline(unittest.TestCase, TestConfigSourceHelper): # Note: bool is "half-way" supported. Only value-less parameters # are typed as bool (eg "--force", not "--force=True") supported_types = (str, list, bool) simple_cmdline = ['--home=mydata', '--processes=4', '--force', # note implicit boolean typing '--extra=foo', '--extra=bar', '--expires=2014-10-15', '--lastrun=2014-10-15 14:32:07'] complex_cmdline = ['--home=mydata', '--processes=4', '--force=True', '--extra=foo', '--extra=bar', '--mymodule-force=False', '--mymodule-extra=foo', '--mymodule-extra=baz', '--mymodule-expires=2014-10-15', '--mymodule-arbitrary-nesting-depth=works', '--extramodule-unique'] def setUp(self): super(TestCommandline, self).setUp() # this means we lack typing information self.simple = Commandline(self.simple_cmdline) self.complex = Commandline(self.complex_cmdline) # Overrides of TestHelper.test_get, .test_typed and and due to # limitations of Commandline (carries almost no typeinfo) def test_get(self): self.assertEqual(self.simple.get("home"), "mydata") self.assertEqual(self.simple.get("processes"), "4") self.assertEqual(self.simple.get("force"), True) self.assertEqual(self.simple.get("extra"), ['foo','bar']) # note typed! self.assertEqual(self.simple.get("expires"), "2014-10-15") self.assertEqual(self.simple.get("lastrun"), "2014-10-15 14:32:07") def test_typed(self): for key in self.simple.keys(): # these should be typed as bool and list, respectively if key in ("force", "extra"): self.assertTrue(self.simple.typed(key)) else: self.assertFalse(self.simple.typed(key)) def test_config_subsections(self): # this case uses valued parameter for --force et al, which # cannot be reliably converted to bools using only intrinsic # information self.supported_types = (str, list) super(TestCommandline, self).test_config_subsections() def test_set(self): self.simple.set("home", "away from home") self.assertEqual(self.simple.get("home"), "away from home")
inifile = INIFile("myapp.ini") # end inifile # begin argparse parser = argparse.ArgumentParser("This is a simple program") parser.add_argument("--home", help="The home directory of the app") parser.add_argument('--dostuff', action="store_true", help="Do some work") parser.add_argument("-t", "--times", type=int, help="Number of times to do it") parser.add_argument('--things', action="append", help="Extra things to crunch") parser.add_argument('--retry', action="store_true", help="Try again") parser.add_argument("file", metavar="FILE", help="The filename to process") # end argparse # begin layeredconfig sys.argv = ['./myapp.py', '--home=/opt/myapp', '-t=2', '--dostuff', 'file.txt'] cfg = LayeredConfig(defaults, inifile, Commandline(parser=parser)) print("Starting %s in %s for %r times (doing work: %s)" % (cfg.name, cfg.home, cfg.times, cfg.dostuff)) # should print "Starting MyApp in /opt/myapp for 2 times (doing work: True)" # end layeredconfig # begin showhelp sys.argv = ['./myapp.py', '-h'] cfg = LayeredConfig(defaults, inifile, Commandline(parser=parser)) # end showhelp # begin nodefaults # NOTE: we never reach this because the previous call to -h will have # called sys.exit
# 1. hard-coded defaults defaults = {"hello": "is it me you're looking for?"} # 2. INI configuration file with open("myapp.ini", "w") as fp: fp.write(""" [__root__] hello = kitty """) # 3. enironment variables import os os.environ['MYAPP_HELLO'] = 'goodbye' # 4.command-line arguments import sys sys.argv = ['./myapp.py', '--hello=world'] # Create a config object that gets settings from these four # sources. config = LayeredConfig(Defaults(defaults), INIFile("myapp.ini"), Environment(prefix="MYAPP_"), Commandline()) # Prints "Hello world!", i.e the value provided by command-line # arguments. Latter sources take precedence over earlier sources. print("Hello %s!" % config.hello) # end firststep return_value = True
'lastrun': datetime(2014, 10, 30, 16, 40, 22) } }) # end defaults # begin inifile myinifile = INIFile("myapp.ini") # end inifile # begin environment env = {'MYAPP_HOME': 'C:\\Progra~1\\MyApp', 'MYAPP_SUBMODULE_RETRY': 'True'} myenv = Environment(env, prefix="MYAPP_") # end environment # begin commandline mycmdline = Commandline(['-f', '--home=/opt/myapp', '--times=2', '--dostuff']) rest = mycmdline.rest # end commandline assert rest == ['-f'] # begin makeconfig cfg = LayeredConfig(mydefaults, myinifile, myenv, mycmdline) # end makeconfig import os def do_stuff(action, idx): pass # begin useconfig