@@ -105,7 +105,23 @@ def _test():
105
105
from io import StringIO , IncrementalNewlineDecoder
106
106
from collections import namedtuple
107
107
108
- TestResults = namedtuple ('TestResults' , 'failed attempted' )
108
+
109
+ class TestResults (namedtuple ('TestResults' , 'failed attempted' )):
110
+ def __new__ (cls , failed , attempted , * , skipped = 0 ):
111
+ results = super ().__new__ (cls , failed , attempted )
112
+ results .skipped = skipped
113
+ return results
114
+
115
+ def __repr__ (self ):
116
+ if self .skipped :
117
+ return (f'TestResults(failed={ self .failed } , '
118
+ f'attempted={ self .attempted } , '
119
+ f'skipped={ self .skipped } )' )
120
+ else :
121
+ # Leave the repr() unchanged for backward compatibility
122
+ # if skipped is zero
123
+ return super ().__repr__ ()
124
+
109
125
110
126
# There are 4 basic classes:
111
127
# - Example: a <source, want> pair, plus an intra-docstring line number.
@@ -1150,8 +1166,7 @@ class DocTestRunner:
1150
1166
"""
1151
1167
A class used to run DocTest test cases, and accumulate statistics.
1152
1168
The `run` method is used to process a single DocTest case. It
1153
- returns a tuple `(f, t)`, where `t` is the number of test cases
1154
- tried, and `f` is the number of test cases that failed.
1169
+ returns a TestResults instance.
1155
1170
1156
1171
>>> tests = DocTestFinder().find(_TestClass)
1157
1172
>>> runner = DocTestRunner(verbose=False)
@@ -1164,8 +1179,8 @@ class DocTestRunner:
1164
1179
_TestClass.square -> TestResults(failed=0, attempted=1)
1165
1180
1166
1181
The `summarize` method prints a summary of all the test cases that
1167
- have been run by the runner, and returns an aggregated `(f, t)`
1168
- tuple :
1182
+ have been run by the runner, and returns an aggregated TestResults
1183
+ instance :
1169
1184
1170
1185
>>> runner.summarize(verbose=1)
1171
1186
4 items passed all tests:
@@ -1178,13 +1193,15 @@ class DocTestRunner:
1178
1193
Test passed.
1179
1194
TestResults(failed=0, attempted=7)
1180
1195
1181
- The aggregated number of tried examples and failed examples is
1182
- also available via the `tries` and `failures ` attributes:
1196
+ The aggregated number of tried examples and failed examples is also
1197
+ available via the `tries`, `failures` and `skips ` attributes:
1183
1198
1184
1199
>>> runner.tries
1185
1200
7
1186
1201
>>> runner.failures
1187
1202
0
1203
+ >>> runner.skips
1204
+ 0
1188
1205
1189
1206
The comparison between expected outputs and actual outputs is done
1190
1207
by an `OutputChecker`. This comparison may be customized with a
@@ -1233,7 +1250,8 @@ def __init__(self, checker=None, verbose=None, optionflags=0):
1233
1250
# Keep track of the examples we've run.
1234
1251
self .tries = 0
1235
1252
self .failures = 0
1236
- self ._name2ft = {}
1253
+ self .skips = 0
1254
+ self ._stats = {}
1237
1255
1238
1256
# Create a fake output target for capturing doctest output.
1239
1257
self ._fakeout = _SpoofOut ()
@@ -1302,13 +1320,11 @@ def __run(self, test, compileflags, out):
1302
1320
Run the examples in `test`. Write the outcome of each example
1303
1321
with one of the `DocTestRunner.report_*` methods, using the
1304
1322
writer function `out`. `compileflags` is the set of compiler
1305
- flags that should be used to execute examples. Return a tuple
1306
- `(f, t)`, where `t` is the number of examples tried, and `f`
1307
- is the number of examples that failed. The examples are run
1308
- in the namespace `test.globs`.
1323
+ flags that should be used to execute examples. Return a TestResults
1324
+ instance. The examples are run in the namespace `test.globs`.
1309
1325
"""
1310
- # Keep track of the number of failures and tries .
1311
- failures = tries = 0
1326
+ # Keep track of the number of failed, attempted, skipped examples .
1327
+ failures = attempted = skips = 0
1312
1328
1313
1329
# Save the option flags (since option directives can be used
1314
1330
# to modify them).
@@ -1320,6 +1336,7 @@ def __run(self, test, compileflags, out):
1320
1336
1321
1337
# Process each example.
1322
1338
for examplenum , example in enumerate (test .examples ):
1339
+ attempted += 1
1323
1340
1324
1341
# If REPORT_ONLY_FIRST_FAILURE is set, then suppress
1325
1342
# reporting after the first failure.
@@ -1337,10 +1354,10 @@ def __run(self, test, compileflags, out):
1337
1354
1338
1355
# If 'SKIP' is set, then skip this example.
1339
1356
if self .optionflags & SKIP :
1357
+ skips += 1
1340
1358
continue
1341
1359
1342
1360
# Record that we started this example.
1343
- tries += 1
1344
1361
if not quiet :
1345
1362
self .report_start (out , test , example )
1346
1363
@@ -1418,19 +1435,22 @@ def __run(self, test, compileflags, out):
1418
1435
# Restore the option flags (in case they were modified)
1419
1436
self .optionflags = original_optionflags
1420
1437
1421
- # Record and return the number of failures and tries .
1422
- self .__record_outcome (test , failures , tries )
1423
- return TestResults (failures , tries )
1438
+ # Record and return the number of failures and attempted .
1439
+ self .__record_outcome (test , failures , attempted , skips )
1440
+ return TestResults (failures , attempted , skipped = skips )
1424
1441
1425
- def __record_outcome (self , test , f , t ):
1442
+ def __record_outcome (self , test , failures , tries , skips ):
1426
1443
"""
1427
- Record the fact that the given DocTest (`test`) generated `f `
1428
- failures out of `t ` tried examples.
1444
+ Record the fact that the given DocTest (`test`) generated `failures `
1445
+ failures out of `tries ` tried examples.
1429
1446
"""
1430
- f2 , t2 = self ._name2ft .get (test .name , (0 ,0 ))
1431
- self ._name2ft [test .name ] = (f + f2 , t + t2 )
1432
- self .failures += f
1433
- self .tries += t
1447
+ failures2 , tries2 , skips2 = self ._stats .get (test .name , (0 , 0 , 0 ))
1448
+ self ._stats [test .name ] = (failures + failures2 ,
1449
+ tries + tries2 ,
1450
+ skips + skips2 )
1451
+ self .failures += failures
1452
+ self .tries += tries
1453
+ self .skips += skips
1434
1454
1435
1455
__LINECACHE_FILENAME_RE = re .compile (r'<doctest '
1436
1456
r'(?P<name>.+)'
@@ -1519,9 +1539,7 @@ def out(s):
1519
1539
def summarize (self , verbose = None ):
1520
1540
"""
1521
1541
Print a summary of all the test cases that have been run by
1522
- this DocTestRunner, and return a tuple `(f, t)`, where `f` is
1523
- the total number of failed examples, and `t` is the total
1524
- number of tried examples.
1542
+ this DocTestRunner, and return a TestResults instance.
1525
1543
1526
1544
The optional `verbose` argument controls how detailed the
1527
1545
summary is. If the verbosity is not specified, then the
@@ -1532,59 +1550,61 @@ def summarize(self, verbose=None):
1532
1550
notests = []
1533
1551
passed = []
1534
1552
failed = []
1535
- totalt = totalf = 0
1536
- for x in self ._name2ft .items ():
1537
- name , (f , t ) = x
1538
- assert f <= t
1539
- totalt += t
1540
- totalf += f
1541
- if t == 0 :
1553
+ total_tries = total_failures = total_skips = 0
1554
+ for item in self ._stats .items ():
1555
+ name , (failures , tries , skips ) = item
1556
+ assert failures <= tries
1557
+ total_tries += tries
1558
+ total_failures += failures
1559
+ total_skips += skips
1560
+ if tries == 0 :
1542
1561
notests .append (name )
1543
- elif f == 0 :
1544
- passed .append ( (name , t ) )
1562
+ elif failures == 0 :
1563
+ passed .append ((name , tries ) )
1545
1564
else :
1546
- failed .append (x )
1565
+ failed .append (item )
1547
1566
if verbose :
1548
1567
if notests :
1549
- print (len (notests ), " items had no tests:" )
1568
+ print (f" { len (notests )} items had no tests:" )
1550
1569
notests .sort ()
1551
- for thing in notests :
1552
- print (" " , thing )
1570
+ for name in notests :
1571
+ print (f " { name } " )
1553
1572
if passed :
1554
- print (len (passed ), " items passed all tests:" )
1573
+ print (f" { len (passed )} items passed all tests:" )
1555
1574
passed .sort ()
1556
- for thing , count in passed :
1557
- print (" %3d tests in %s" % ( count , thing ) )
1575
+ for name , count in passed :
1576
+ print (f" { count :3d } tests in { name } " )
1558
1577
if failed :
1559
1578
print (self .DIVIDER )
1560
- print (len (failed ), " items had failures:" )
1579
+ print (f" { len (failed )} items had failures:" )
1561
1580
failed .sort ()
1562
- for thing , (f , t ) in failed :
1563
- print (" %3d of %3d in %s" % ( f , t , thing ) )
1581
+ for name , (failures , tries , skips ) in failed :
1582
+ print (f" { failures :3d } of { tries :3d } in { name } " )
1564
1583
if verbose :
1565
- print (totalt , "tests in" , len (self ._name2ft ), "items." )
1566
- print (totalt - totalf , "passed and" , totalf , "failed." )
1567
- if totalf :
1568
- print ("***Test Failed***" , totalf , "failures." )
1584
+ print (f"{ total_tries } tests in { len (self ._stats )} items." )
1585
+ print (f"{ total_tries - total_failures } passed and { total_failures } failed." )
1586
+ if total_failures :
1587
+ msg = f"***Test Failed*** { total_failures } failures"
1588
+ if total_skips :
1589
+ msg = f"{ msg } and { total_skips } skipped tests"
1590
+ print (f"{ msg } ." )
1569
1591
elif verbose :
1570
1592
print ("Test passed." )
1571
- return TestResults (totalf , totalt )
1593
+ return TestResults (total_failures , total_tries , skipped = total_skips )
1572
1594
1573
1595
#/////////////////////////////////////////////////////////////////
1574
1596
# Backward compatibility cruft to maintain doctest.master.
1575
1597
#/////////////////////////////////////////////////////////////////
1576
1598
def merge (self , other ):
1577
- d = self ._name2ft
1578
- for name , (f , t ) in other ._name2ft .items ():
1599
+ d = self ._stats
1600
+ for name , (failures , tries , skips ) in other ._stats .items ():
1579
1601
if name in d :
1580
- # Don't print here by default, since doing
1581
- # so breaks some of the buildbots
1582
- #print("*** DocTestRunner.merge: '" + name + "' in both" \
1583
- # " testers; summing outcomes.")
1584
- f2 , t2 = d [name ]
1585
- f = f + f2
1586
- t = t + t2
1587
- d [name ] = f , t
1602
+ failures2 , tries2 , skips2 = d [name ]
1603
+ failures = failures + failures2
1604
+ tries = tries + tries2
1605
+ skips = skips + skips2
1606
+ d [name ] = (failures , tries , skips )
1607
+
1588
1608
1589
1609
class OutputChecker :
1590
1610
"""
@@ -1984,7 +2004,8 @@ class doctest.Tester, then merges the results into (or creates)
1984
2004
else :
1985
2005
master .merge (runner )
1986
2006
1987
- return TestResults (runner .failures , runner .tries )
2007
+ return TestResults (runner .failures , runner .tries , skipped = runner .skips )
2008
+
1988
2009
1989
2010
def testfile (filename , module_relative = True , name = None , package = None ,
1990
2011
globs = None , verbose = None , report = True , optionflags = 0 ,
@@ -2107,7 +2128,8 @@ class doctest.Tester, then merges the results into (or creates)
2107
2128
else :
2108
2129
master .merge (runner )
2109
2130
2110
- return TestResults (runner .failures , runner .tries )
2131
+ return TestResults (runner .failures , runner .tries , skipped = runner .skips )
2132
+
2111
2133
2112
2134
def run_docstring_examples (f , globs , verbose = False , name = "NoName" ,
2113
2135
compileflags = None , optionflags = 0 ):
0 commit comments