(root)/
Python-3.12.0/
Lib/
unittest/
main.py
       1  """Unittest main program"""
       2  
       3  import sys
       4  import argparse
       5  import os
       6  import warnings
       7  
       8  from . import loader, runner
       9  from .signals import installHandler
      10  
      11  __unittest = True
      12  _NO_TESTS_EXITCODE = 5
      13  
      14  MAIN_EXAMPLES = """\
      15  Examples:
      16    %(prog)s test_module               - run tests from test_module
      17    %(prog)s module.TestClass          - run tests from module.TestClass
      18    %(prog)s module.Class.test_method  - run specified test method
      19    %(prog)s path/to/test_file.py      - run tests from test_file.py
      20  """
      21  
      22  MODULE_EXAMPLES = """\
      23  Examples:
      24    %(prog)s                           - run default set of tests
      25    %(prog)s MyTestSuite               - run suite 'MyTestSuite'
      26    %(prog)s MyTestCase.testSomething  - run MyTestCase.testSomething
      27    %(prog)s MyTestCase                - run all 'test*' test methods
      28                                         in MyTestCase
      29  """
      30  
      31  def _convert_name(name):
      32      # on Linux / Mac OS X 'foo.PY' is not importable, but on
      33      # Windows it is. Simpler to do a case insensitive match
      34      # a better check would be to check that the name is a
      35      # valid Python module name.
      36      if os.path.isfile(name) and name.lower().endswith('.py'):
      37          if os.path.isabs(name):
      38              rel_path = os.path.relpath(name, os.getcwd())
      39              if os.path.isabs(rel_path) or rel_path.startswith(os.pardir):
      40                  return name
      41              name = rel_path
      42          # on Windows both '\' and '/' are used as path
      43          # separators. Better to replace both than rely on os.path.sep
      44          return os.path.normpath(name)[:-3].replace('\\', '.').replace('/', '.')
      45      return name
      46  
      47  def _convert_names(names):
      48      return [_convert_name(name) for name in names]
      49  
      50  
      51  def _convert_select_pattern(pattern):
      52      if not '*' in pattern:
      53          pattern = '*%s*' % pattern
      54      return pattern
      55  
      56  
      57  class ESC[4;38;5;81mTestProgram(ESC[4;38;5;149mobject):
      58      """A command-line program that runs a set of tests; this is primarily
      59         for making test modules conveniently executable.
      60      """
      61      # defaults for testing
      62      module=None
      63      verbosity = 1
      64      failfast = catchbreak = buffer = progName = warnings = testNamePatterns = None
      65      _discovery_parser = None
      66  
      67      def __init__(self, module='__main__', defaultTest=None, argv=None,
      68                      testRunner=None, testLoader=loader.defaultTestLoader,
      69                      exit=True, verbosity=1, failfast=None, catchbreak=None,
      70                      buffer=None, warnings=None, *, tb_locals=False,
      71                      durations=None):
      72          if isinstance(module, str):
      73              self.module = __import__(module)
      74              for part in module.split('.')[1:]:
      75                  self.module = getattr(self.module, part)
      76          else:
      77              self.module = module
      78          if argv is None:
      79              argv = sys.argv
      80  
      81          self.exit = exit
      82          self.failfast = failfast
      83          self.catchbreak = catchbreak
      84          self.verbosity = verbosity
      85          self.buffer = buffer
      86          self.tb_locals = tb_locals
      87          self.durations = durations
      88          if warnings is None and not sys.warnoptions:
      89              # even if DeprecationWarnings are ignored by default
      90              # print them anyway unless other warnings settings are
      91              # specified by the warnings arg or the -W python flag
      92              self.warnings = 'default'
      93          else:
      94              # here self.warnings is set either to the value passed
      95              # to the warnings args or to None.
      96              # If the user didn't pass a value self.warnings will
      97              # be None. This means that the behavior is unchanged
      98              # and depends on the values passed to -W.
      99              self.warnings = warnings
     100          self.defaultTest = defaultTest
     101          self.testRunner = testRunner
     102          self.testLoader = testLoader
     103          self.progName = os.path.basename(argv[0])
     104          self.parseArgs(argv)
     105          self.runTests()
     106  
     107      def usageExit(self, msg=None):
     108          warnings.warn("TestProgram.usageExit() is deprecated and will be"
     109                        " removed in Python 3.13", DeprecationWarning)
     110          if msg:
     111              print(msg)
     112          if self._discovery_parser is None:
     113              self._initArgParsers()
     114          self._print_help()
     115          sys.exit(2)
     116  
     117      def _print_help(self, *args, **kwargs):
     118          if self.module is None:
     119              print(self._main_parser.format_help())
     120              print(MAIN_EXAMPLES % {'prog': self.progName})
     121              self._discovery_parser.print_help()
     122          else:
     123              print(self._main_parser.format_help())
     124              print(MODULE_EXAMPLES % {'prog': self.progName})
     125  
     126      def parseArgs(self, argv):
     127          self._initArgParsers()
     128          if self.module is None:
     129              if len(argv) > 1 and argv[1].lower() == 'discover':
     130                  self._do_discovery(argv[2:])
     131                  return
     132              self._main_parser.parse_args(argv[1:], self)
     133              if not self.tests:
     134                  # this allows "python -m unittest -v" to still work for
     135                  # test discovery.
     136                  self._do_discovery([])
     137                  return
     138          else:
     139              self._main_parser.parse_args(argv[1:], self)
     140  
     141          if self.tests:
     142              self.testNames = _convert_names(self.tests)
     143              if __name__ == '__main__':
     144                  # to support python -m unittest ...
     145                  self.module = None
     146          elif self.defaultTest is None:
     147              # createTests will load tests from self.module
     148              self.testNames = None
     149          elif isinstance(self.defaultTest, str):
     150              self.testNames = (self.defaultTest,)
     151          else:
     152              self.testNames = list(self.defaultTest)
     153          self.createTests()
     154  
     155      def createTests(self, from_discovery=False, Loader=None):
     156          if self.testNamePatterns:
     157              self.testLoader.testNamePatterns = self.testNamePatterns
     158          if from_discovery:
     159              loader = self.testLoader if Loader is None else Loader()
     160              self.test = loader.discover(self.start, self.pattern, self.top)
     161          elif self.testNames is None:
     162              self.test = self.testLoader.loadTestsFromModule(self.module)
     163          else:
     164              self.test = self.testLoader.loadTestsFromNames(self.testNames,
     165                                                             self.module)
     166  
     167      def _initArgParsers(self):
     168          parent_parser = self._getParentArgParser()
     169          self._main_parser = self._getMainArgParser(parent_parser)
     170          self._discovery_parser = self._getDiscoveryArgParser(parent_parser)
     171  
     172      def _getParentArgParser(self):
     173          parser = argparse.ArgumentParser(add_help=False)
     174  
     175          parser.add_argument('-v', '--verbose', dest='verbosity',
     176                              action='store_const', const=2,
     177                              help='Verbose output')
     178          parser.add_argument('-q', '--quiet', dest='verbosity',
     179                              action='store_const', const=0,
     180                              help='Quiet output')
     181          parser.add_argument('--locals', dest='tb_locals',
     182                              action='store_true',
     183                              help='Show local variables in tracebacks')
     184          parser.add_argument('--durations', dest='durations', type=int,
     185                              default=None, metavar="N",
     186                              help='Show the N slowest test cases (N=0 for all)')
     187          if self.failfast is None:
     188              parser.add_argument('-f', '--failfast', dest='failfast',
     189                                  action='store_true',
     190                                  help='Stop on first fail or error')
     191              self.failfast = False
     192          if self.catchbreak is None:
     193              parser.add_argument('-c', '--catch', dest='catchbreak',
     194                                  action='store_true',
     195                                  help='Catch Ctrl-C and display results so far')
     196              self.catchbreak = False
     197          if self.buffer is None:
     198              parser.add_argument('-b', '--buffer', dest='buffer',
     199                                  action='store_true',
     200                                  help='Buffer stdout and stderr during tests')
     201              self.buffer = False
     202          if self.testNamePatterns is None:
     203              parser.add_argument('-k', dest='testNamePatterns',
     204                                  action='append', type=_convert_select_pattern,
     205                                  help='Only run tests which match the given substring')
     206              self.testNamePatterns = []
     207  
     208          return parser
     209  
     210      def _getMainArgParser(self, parent):
     211          parser = argparse.ArgumentParser(parents=[parent])
     212          parser.prog = self.progName
     213          parser.print_help = self._print_help
     214  
     215          parser.add_argument('tests', nargs='*',
     216                              help='a list of any number of test modules, '
     217                              'classes and test methods.')
     218  
     219          return parser
     220  
     221      def _getDiscoveryArgParser(self, parent):
     222          parser = argparse.ArgumentParser(parents=[parent])
     223          parser.prog = '%s discover' % self.progName
     224          parser.epilog = ('For test discovery all test modules must be '
     225                           'importable from the top level directory of the '
     226                           'project.')
     227  
     228          parser.add_argument('-s', '--start-directory', dest='start',
     229                              help="Directory to start discovery ('.' default)")
     230          parser.add_argument('-p', '--pattern', dest='pattern',
     231                              help="Pattern to match tests ('test*.py' default)")
     232          parser.add_argument('-t', '--top-level-directory', dest='top',
     233                              help='Top level directory of project (defaults to '
     234                                   'start directory)')
     235          for arg in ('start', 'pattern', 'top'):
     236              parser.add_argument(arg, nargs='?',
     237                                  default=argparse.SUPPRESS,
     238                                  help=argparse.SUPPRESS)
     239  
     240          return parser
     241  
     242      def _do_discovery(self, argv, Loader=None):
     243          self.start = '.'
     244          self.pattern = 'test*.py'
     245          self.top = None
     246          if argv is not None:
     247              # handle command line args for test discovery
     248              if self._discovery_parser is None:
     249                  # for testing
     250                  self._initArgParsers()
     251              self._discovery_parser.parse_args(argv, self)
     252  
     253          self.createTests(from_discovery=True, Loader=Loader)
     254  
     255      def runTests(self):
     256          if self.catchbreak:
     257              installHandler()
     258          if self.testRunner is None:
     259              self.testRunner = runner.TextTestRunner
     260          if isinstance(self.testRunner, type):
     261              try:
     262                  try:
     263                      testRunner = self.testRunner(verbosity=self.verbosity,
     264                                                   failfast=self.failfast,
     265                                                   buffer=self.buffer,
     266                                                   warnings=self.warnings,
     267                                                   tb_locals=self.tb_locals,
     268                                                   durations=self.durations)
     269                  except TypeError:
     270                      # didn't accept the tb_locals or durations argument
     271                      testRunner = self.testRunner(verbosity=self.verbosity,
     272                                                   failfast=self.failfast,
     273                                                   buffer=self.buffer,
     274                                                   warnings=self.warnings)
     275              except TypeError:
     276                  # didn't accept the verbosity, buffer or failfast arguments
     277                  testRunner = self.testRunner()
     278          else:
     279              # it is assumed to be a TestRunner instance
     280              testRunner = self.testRunner
     281          self.result = testRunner.run(self.test)
     282          if self.exit:
     283              if self.result.testsRun == 0:
     284                  sys.exit(_NO_TESTS_EXITCODE)
     285              elif self.result.wasSuccessful():
     286                  sys.exit(0)
     287              else:
     288                  sys.exit(1)
     289  
     290  
     291  main = TestProgram