Permalink
Browse files

Implemented config file support (#18) based on patch from Antoine Pit…

…rou.
  • Loading branch information...
1 parent 00adb58 commit 368bd98c8fc9e1e85910263fb22ee90f0bf2c7c1 @jpellerin jpellerin committed Apr 10, 2007
@@ -0,0 +1,2 @@
+[nosetests]
+verbosity=10
@@ -0,0 +1,25 @@
+import os
+import unittest
+from nose.config import Config
+
+support = os.path.join(os.path.dirname(__file__), 'support')
+
+class TestConfigurationFromFile(unittest.TestCase):
+ def setUp(self):
+ self.cfg_file = os.path.join(support, 'test.cfg')
+
+ def test_load_config_file(self):
+ c = Config(files=self.cfg_file)
+ c.configure(['test_load_config_file'])
+ self.assertEqual(c.verbosity, 10)
+
+ def test_config_file_set_by_arg(self):
+ c = Config()
+ c.configure(['test_config_file_set_by_arg',
+ '-c', self.cfg_file, '-v'])
+ # 10 from file, 1 more from cmd line
+ self.assertEqual(c.verbosity, 11)
+
+
+if __name__ == '__main__':
+ unittest.main()
@@ -56,6 +56,8 @@ def test_run_support_package2(self):
assert res.wasSuccessful
assert not res.errors
assert not res.failures
+
+
if __name__ == '__main__':
import logging
@@ -40,7 +40,7 @@ def test(self):
test = case.Test(TC('test'), resultProxy=rpf)
test(res)
- assert not res.errors, res.errors, "Skip was recorded as error"
+ assert not res.errors, "Skip was recorded as error %s" % res.errors
assert not debug.pdb.called, "pdb was called"
View
@@ -2,11 +2,17 @@
import os
import re
import sys
+import ConfigParser
from optparse import OptionParser
from nose.util import absdir, tolist
+from warnings import warn
log = logging.getLogger(__name__)
+# not allowed in config files
+option_blacklist = ['help', 'verbose']
+
+
class Config(object):
"""nose configuration.
@@ -36,6 +42,7 @@ def __init__(self, **kw):
self.args = ()
self.testMatch = re.compile(r'(?:^|[\b_\.%s-])[Tt]est' % os.sep)
self.addPaths = not env.get('NOSE_NOPATH', False)
+ self.configSection = 'nosetests'
self.detailedErrors = env.get('NOSE_DETAILED_ERRORS', False)
self.debug = env.get('NOSE_DEBUG')
self.debugLog = env.get('NOSE_DEBUG_LOG')
@@ -88,13 +95,22 @@ def configure(self, argv=None, doc=None):
if argv is None:
argv = sys.argv
+
+ if hasattr(self, 'files'):
+ argv = self.loadConfig(self.files, argv)
+
env = self.env
- self.getParser(doc)
+ parser = self.getParser(doc)
self.plugins.loadPlugins()
- self.pluginOpts()
+ self.pluginOpts(parser)
- options, args = self.parser.parse_args(argv)
+ options, args = parser.parse_args(argv)
+ # If -c --config has been specified on command line,
+ # load those config files to create a new argv set and reparse
+ if options.files:
+ argv = self.loadConfig(options.files, argv)
+ options, args = parser.parse_args(argv)
try:
self.options, self.testNames = options, args[1:]
except IndexError:
@@ -169,6 +185,13 @@ def getParser(self, doc=None):
type="int", help="Set verbosity; --verbosity=2 is "
"the same as -vv")
parser.add_option(
+ "-q", "--quiet", action="store_const", const=0, dest="verbosity")
+ parser.add_option(
+ "-c", "--config", action="append", dest="files",
+ help="Load configuration from config file(s). May be specified "
+ "multiple times; in that case, all config files will be "
+ "loaded and combined")
+ parser.add_option(
"-l", "--debug", action="store",
dest="debug", default=self.debug,
help="Activate debug logging for one or more systems. "
@@ -181,14 +204,6 @@ def getParser(self, doc=None):
help="Log debug messages to this file "
"(default: sys.stderr)")
parser.add_option(
- "-q", "--quiet", action="store_const", const=0, dest="verbosity")
- parser.add_option(
- "-w", "--where", action="append", dest="where",
- help="DEPRECATED Look for tests in this directory. "
- "This option is deprecated; you can pass the directories "
- "without using -w for the same behavior. [NOSE_WHERE]"
- )
- parser.add_option(
"-e", "--exclude", action="append", dest="exclude",
help="Don't run tests that match regular "
"expression [NOSE_EXCLUDE]")
@@ -224,11 +239,50 @@ def getParser(self, doc=None):
help="DO NOT look for tests in python modules that are "
"executable. (The default on the windows platform is to "
"do so.)")
- self.parser = parser
+ parser.add_option(
+ "-w", "--where", action="append", dest="where",
+ help="DEPRECATED Look for tests in this directory. "
+ "This option is deprecated; you can pass the directories "
+ "without using -w for the same behavior. [NOSE_WHERE]"
+ )
return parser
- def pluginOpts(self):
- self.plugins.addOptions(self.parser, self.env)
+ def loadConfig(self, file, argv):
+ """Load config from file (may be filename or file-like object) and
+ push the config into argv.
+ """
+ cfg = ConfigParser.RawConfigParser()
+ try:
+ try:
+ cfg.readfp(file)
+ except AttributeError:
+ # Filename not an fp
+ cfg.read(file)
+ except ConfigParser.Error, e:
+ warn("Error reading config file %s: %s" % (file, e),
+ RuntimeWarning)
+ return argv
+ if self.configSection not in cfg.sections():
+ return argv
+ file_argv = []
+ for optname in cfg.options(self.configSection):
+ if optname in option_blacklist:
+ continue
+ value = cfg.get(self.configSection, optname)
+ if flag(value):
+ if _bool(value):
+ file_argv.append('--' + optname)
+ else:
+ file_argv.append('--' + optname)
+ file_argv.append(value)
+ # Copy the given args and insert args loaded from file
+ # between the program name (first arg) and the rest
+ combined = argv[:]
+ combined[1:1] = file_argv
+ return combined
+
+ def pluginOpts(self, parser):
+ self.plugins.addOptions(parser, self.env)
def reset(self):
self.__dict__.update(self._orig)
@@ -251,6 +305,18 @@ def __call__(self, *arg, **kw):
return
+# used when parsing config files
+def flag(val):
+ """Does the value look like an on/off flag?"""
+ if len(val) > 5:
+ return False
+ return val.upper() in ('1', '0', 'F', 'T', 'TRUE', 'FALSE', 'ON', 'OFF')
+
+
+def _bool(val):
+ return val.upper() in ('1', 'T', 'TRUE', 'ON')
+
+
# deprecated
# FIXME maybe kill all this and instantiate a new config for each
View
@@ -182,18 +182,33 @@ class TestProgram(unittest.TestProgram):
that control or produce output) detailed in the options below.
"""
verbosity = 1
+ userConfigFiles = [
+ # Linux users will prefer this
+ "~/.noserc",
+ # Windows users will prefer this
+ "~/nose.cfg"
+ ]
def __init__(self, module=None, defaultTest='.', argv=None,
testRunner=None, testLoader=None, env=None, config=None):
if env is None:
env = os.environ
if config is None:
- config = Config(env=env, plugins=DefaultPluginManager())
+ config = self.makeConfig(env)
self.config = config
unittest.TestProgram.__init__(
self, module=module, defaultTest=defaultTest,
argv=argv, testRunner=testRunner, testLoader=testLoader)
-
+
+ def makeConfig(self, env):
+ """Load a Config, pre-filled with user config files if any are
+ found.
+ """
+ cfg_files = filter(os.path.exists,
+ map(os.path.expanduser, self.userConfigFiles) +
+ ['setup.cfg'])
+ return Config(
+ env=env, files=cfg_files, plugins=DefaultPluginManager())
def parseArgs(self, argv):
"""Parse argv and env and configure running environment.
@@ -254,30 +269,6 @@ def runTests(self):
return self.success
-## # FIXME move this
-## # add opts from plugins
-## all_plugins = []
-## # when generating the help message, load only builtin plugins
-## for plugcls in load_plugins():
-## plug = plugcls()
-## try:
-## plug.add_options(parser, env)
-## except AttributeError:
-## pass
-
-
-
-
-# FIXME use plugin manager
-# try:
-# # give plugins a chance to start
-# call_plugins(conf.plugins, 'begin')
-# except:
-# if conf.capture:
-# end_capture()
-# raise
-
-
def configure_logging(options):
"""Configure logging for nose, or optionally other packages. Any logger
name may be set with the debug option, and that logger will be set to
View
@@ -97,6 +97,7 @@ def chain(self, *arg, **kw):
"""Call plugins in a chain, where the result of each plugin call is
sent to the next plugin as input. The final output result is returned.
"""
+ result = None
for p in self.plugins:
meth = getattr(p, self.call, None)
if meth is None:

0 comments on commit 368bd98

Please sign in to comment.