55import types
66
77from . import loader , runner
8+ from .signals import installHandler
89
910__unittest = True
1011
1112
13+ FAILFAST = " -f, --failfast Stop on first failure\n "
14+ CATCHBREAK = " -c, --catch Catch control-C and display results\n "
15+
1216USAGE_AS_MAIN = """\
1317 Usage: %(progName)s [options] [tests]
1418
1519Options:
1620 -h, --help Show this message
1721 -v, --verbose Verbose output
1822 -q, --quiet Minimal output
19- -f, --failfast Stop on first failure
20-
23+ %(failfast)s%(catchbreak)s
2124Examples:
2225 %(progName)s test_module - run tests from test_module
2326 %(progName)s test_module.TestClass - run tests from
3134
3235Options:
3336 -v, --verbose Verbose output
34- -f, --failfast Stop on first failure
35- -s directory Directory to start discovery ('.' default)
37+ %(failfast)s%(catchbreak)s -s directory Directory to start discovery ('.' default)
3638 -p pattern Pattern to match test files ('test*.py' default)
3739 -t directory Top level directory of project (default to
3840 start directory)
4850 -h, --help Show this message
4951 -v, --verbose Verbose output
5052 -q, --quiet Minimal output
51- -f, --failfast Stop on first failure
52-
53+ %(failfast)s%(catchbreak)s
5354Examples:
5455 %(progName)s - run default set of tests
5556 %(progName)s MyTestSuite - run suite 'MyTestSuite'
5859 in MyTestCase
5960"""
6061
62+
63+
6164class TestProgram (object ):
6265 """A command-line program that runs a set of tests; this is primarily
6366 for making test modules conveniently executable.
6467 """
6568 USAGE = USAGE_FROM_MODULE
69+
70+ # defaults for testing
71+ failfast = catchbreak = None
72+
6673 def __init__ (self , module = '__main__' , defaultTest = None ,
6774 argv = None , testRunner = None ,
6875 testLoader = loader .defaultTestLoader , exit = True ,
69- verbosity = 1 , failfast = False ):
76+ verbosity = 1 , failfast = None , catchbreak = None ):
7077 if isinstance (module , str ):
7178 self .module = __import__ (module )
7279 for part in module .split ('.' )[1 :]:
@@ -78,6 +85,7 @@ def __init__(self, module='__main__', defaultTest=None,
7885
7986 self .exit = exit
8087 self .failfast = failfast
88+ self .catchbreak = catchbreak
8189 self .verbosity = verbosity
8290 self .defaultTest = defaultTest
8391 self .testRunner = testRunner
@@ -89,7 +97,12 @@ def __init__(self, module='__main__', defaultTest=None,
8997 def usageExit (self , msg = None ):
9098 if msg :
9199 print (msg )
92- print (self .USAGE % self .__dict__ )
100+ usage = {'progName' : self .progName , 'catchbreak' : '' , 'failfast' : '' }
101+ if self .failfast != False :
102+ usage ['failfast' ] = FAILFAST
103+ if self .catchbreak != False :
104+ usage ['catchbreak' ] = CATCHBREAK
105+ print (self .USAGE % usage )
93106 sys .exit (2 )
94107
95108 def parseArgs (self , argv ):
@@ -98,9 +111,9 @@ def parseArgs(self, argv):
98111 return
99112
100113 import getopt
101- long_opts = ['help' , 'verbose' , 'quiet' , 'failfast' ]
114+ long_opts = ['help' , 'verbose' , 'quiet' , 'failfast' , 'catch' ]
102115 try :
103- options , args = getopt .getopt (argv [1 :], 'hHvqf ' , long_opts )
116+ options , args = getopt .getopt (argv [1 :], 'hHvqfc ' , long_opts )
104117 for opt , value in options :
105118 if opt in ('-h' ,'-H' ,'--help' ):
106119 self .usageExit ()
@@ -109,7 +122,13 @@ def parseArgs(self, argv):
109122 if opt in ('-v' ,'--verbose' ):
110123 self .verbosity = 2
111124 if opt in ('-f' ,'--failfast' ):
112- self .failfast = True
125+ if self .failfast is None :
126+ self .failfast = True
127+ # Should this raise an exception if -f is not valid?
128+ if opt in ('-c' ,'--catch' ):
129+ if self .catchbreak is None :
130+ self .catchbreak = True
131+ # Should this raise an exception if -c is not valid?
113132 if len (args ) == 0 and self .defaultTest is None :
114133 # createTests will load tests from self.module
115134 self .testNames = None
@@ -137,8 +156,14 @@ def _do_discovery(self, argv, Loader=loader.TestLoader):
137156 parser = optparse .OptionParser ()
138157 parser .add_option ('-v' , '--verbose' , dest = 'verbose' , default = False ,
139158 help = 'Verbose output' , action = 'store_true' )
140- parser .add_option ('-f' , '--failfast' , dest = 'failfast' , default = False ,
141- help = 'Stop on first fail or error' , action = 'store_true' )
159+ if self .failfast != False :
160+ parser .add_option ('-f' , '--failfast' , dest = 'failfast' , default = False ,
161+ help = 'Stop on first fail or error' ,
162+ action = 'store_true' )
163+ if self .catchbreak != False :
164+ parser .add_option ('-c' , '--catch' , dest = 'catchbreak' , default = False ,
165+ help = 'Catch ctrl-C and display results so far' ,
166+ action = 'store_true' )
142167 parser .add_option ('-s' , '--start-directory' , dest = 'start' , default = '.' ,
143168 help = "Directory to start discovery ('.' default)" )
144169 parser .add_option ('-p' , '--pattern' , dest = 'pattern' , default = 'test*.py' ,
@@ -153,7 +178,13 @@ def _do_discovery(self, argv, Loader=loader.TestLoader):
153178 for name , value in zip (('start' , 'pattern' , 'top' ), args ):
154179 setattr (options , name , value )
155180
156- self .failfast = options .failfast
181+ # only set options from the parsing here
182+ # if they weren't set explicitly in the constructor
183+ if self .failfast is None :
184+ self .failfast = options .failfast
185+ if self .catchbreak is None :
186+ self .catchbreak = options .catchbreak
187+
157188 if options .verbose :
158189 self .verbosity = 2
159190
@@ -165,6 +196,8 @@ def _do_discovery(self, argv, Loader=loader.TestLoader):
165196 self .test = loader .discover (start_dir , pattern , top_level_dir )
166197
167198 def runTests (self ):
199+ if self .catchbreak :
200+ installHandler ()
168201 if self .testRunner is None :
169202 self .testRunner = runner .TextTestRunner
170203 if isinstance (self .testRunner , type ):
0 commit comments