Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" version info, help messages, tracing configuration. """ 

2import os 

3import sys 

4from argparse import Action 

5from typing import List 

6from typing import Optional 

7from typing import Union 

8 

9import py 

10 

11import pytest 

12from _pytest.config import Config 

13from _pytest.config import ExitCode 

14from _pytest.config import PrintHelp 

15from _pytest.config.argparsing import Parser 

16 

17 

18class HelpAction(Action): 

19 """This is an argparse Action that will raise an exception in 

20 order to skip the rest of the argument parsing when --help is passed. 

21 This prevents argparse from quitting due to missing required arguments 

22 when any are defined, for example by ``pytest_addoption``. 

23 This is similar to the way that the builtin argparse --help option is 

24 implemented by raising SystemExit. 

25 """ 

26 

27 def __init__(self, option_strings, dest=None, default=False, help=None): 

28 super().__init__( 

29 option_strings=option_strings, 

30 dest=dest, 

31 const=True, 

32 default=default, 

33 nargs=0, 

34 help=help, 

35 ) 

36 

37 def __call__(self, parser, namespace, values, option_string=None): 

38 setattr(namespace, self.dest, self.const) 

39 

40 # We should only skip the rest of the parsing after preparse is done 

41 if getattr(parser._parser, "after_preparse", False): 

42 raise PrintHelp 

43 

44 

45def pytest_addoption(parser: Parser) -> None: 

46 group = parser.getgroup("debugconfig") 

47 group.addoption( 

48 "--version", 

49 "-V", 

50 action="count", 

51 default=0, 

52 dest="version", 

53 help="display pytest version and information about plugins." 

54 "When given twice, also display information about plugins.", 

55 ) 

56 group._addoption( 

57 "-h", 

58 "--help", 

59 action=HelpAction, 

60 dest="help", 

61 help="show help message and configuration info", 

62 ) 

63 group._addoption( 

64 "-p", 

65 action="append", 

66 dest="plugins", 

67 default=[], 

68 metavar="name", 

69 help="early-load given plugin module name or entry point (multi-allowed).\n" 

70 "To avoid loading of plugins, use the `no:` prefix, e.g. " 

71 "`no:doctest`.", 

72 ) 

73 group.addoption( 

74 "--traceconfig", 

75 "--trace-config", 

76 action="store_true", 

77 default=False, 

78 help="trace considerations of conftest.py files.", 

79 ) 

80 group.addoption( 

81 "--debug", 

82 action="store_true", 

83 dest="debug", 

84 default=False, 

85 help="store internal tracing debug information in 'pytestdebug.log'.", 

86 ) 

87 group._addoption( 

88 "-o", 

89 "--override-ini", 

90 dest="override_ini", 

91 action="append", 

92 help='override ini option with "option=value" style, e.g. `-o xfail_strict=True -o cache_dir=cache`.', 

93 ) 

94 

95 

96@pytest.hookimpl(hookwrapper=True) 

97def pytest_cmdline_parse(): 

98 outcome = yield 

99 config = outcome.get_result() # type: Config 

100 if config.option.debug: 

101 path = os.path.abspath("pytestdebug.log") 

102 debugfile = open(path, "w") 

103 debugfile.write( 

104 "versions pytest-%s, py-%s, " 

105 "python-%s\ncwd=%s\nargs=%s\n\n" 

106 % ( 

107 pytest.__version__, 

108 py.__version__, 

109 ".".join(map(str, sys.version_info)), 

110 os.getcwd(), 

111 config.invocation_params.args, 

112 ) 

113 ) 

114 config.trace.root.setwriter(debugfile.write) 

115 undo_tracing = config.pluginmanager.enable_tracing() 

116 sys.stderr.write("writing pytestdebug information to %s\n" % path) 

117 

118 def unset_tracing() -> None: 

119 debugfile.close() 

120 sys.stderr.write("wrote pytestdebug information to %s\n" % debugfile.name) 

121 config.trace.root.setwriter(None) 

122 undo_tracing() 

123 

124 config.add_cleanup(unset_tracing) 

125 

126 

127def showversion(config: Config) -> None: 

128 if config.option.version > 1: 

129 sys.stderr.write( 

130 "This is pytest version {}, imported from {}\n".format( 

131 pytest.__version__, pytest.__file__ 

132 ) 

133 ) 

134 plugininfo = getpluginversioninfo(config) 

135 if plugininfo: 

136 for line in plugininfo: 

137 sys.stderr.write(line + "\n") 

138 else: 

139 sys.stderr.write("pytest {}\n".format(pytest.__version__)) 

140 

141 

142def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: 

143 if config.option.version > 0: 

144 showversion(config) 

145 return 0 

146 elif config.option.help: 

147 config._do_configure() 

148 showhelp(config) 

149 config._ensure_unconfigure() 

150 return 0 

151 return None 

152 

153 

154def showhelp(config: Config) -> None: 

155 import textwrap 

156 

157 reporter = config.pluginmanager.get_plugin("terminalreporter") 

158 tw = reporter._tw 

159 tw.write(config._parser.optparser.format_help()) 

160 tw.line() 

161 tw.line( 

162 "[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:" 

163 ) 

164 tw.line() 

165 

166 columns = tw.fullwidth # costly call 

167 indent_len = 24 # based on argparse's max_help_position=24 

168 indent = " " * indent_len 

169 for name in config._parser._ininames: 

170 help, type, default = config._parser._inidict[name] 

171 if type is None: 

172 type = "string" 

173 if help is None: 

174 raise TypeError("help argument cannot be None for {}".format(name)) 

175 spec = "{} ({}):".format(name, type) 

176 tw.write(" %s" % spec) 

177 spec_len = len(spec) 

178 if spec_len > (indent_len - 3): 

179 # Display help starting at a new line. 

180 tw.line() 

181 helplines = textwrap.wrap( 

182 help, 

183 columns, 

184 initial_indent=indent, 

185 subsequent_indent=indent, 

186 break_on_hyphens=False, 

187 ) 

188 

189 for line in helplines: 

190 tw.line(line) 

191 else: 

192 # Display help starting after the spec, following lines indented. 

193 tw.write(" " * (indent_len - spec_len - 2)) 

194 wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) 

195 

196 if wrapped: 

197 tw.line(wrapped[0]) 

198 for line in wrapped[1:]: 

199 tw.line(indent + line) 

200 

201 tw.line() 

202 tw.line("environment variables:") 

203 vars = [ 

204 ("PYTEST_ADDOPTS", "extra command line options"), 

205 ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"), 

206 ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "set to disable plugin auto-loading"), 

207 ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), 

208 ] 

209 for name, help in vars: 

210 tw.line(" {:<24} {}".format(name, help)) 

211 tw.line() 

212 tw.line() 

213 

214 tw.line("to see available markers type: pytest --markers") 

215 tw.line("to see available fixtures type: pytest --fixtures") 

216 tw.line( 

217 "(shown according to specified file_or_dir or current dir " 

218 "if not specified; fixtures with leading '_' are only shown " 

219 "with the '-v' option" 

220 ) 

221 

222 for warningreport in reporter.stats.get("warnings", []): 

223 tw.line("warning : " + warningreport.message, red=True) 

224 return 

225 

226 

227conftest_options = [("pytest_plugins", "list of plugin names to load")] 

228 

229 

230def getpluginversioninfo(config: Config) -> List[str]: 

231 lines = [] 

232 plugininfo = config.pluginmanager.list_plugin_distinfo() 

233 if plugininfo: 

234 lines.append("setuptools registered plugins:") 

235 for plugin, dist in plugininfo: 

236 loc = getattr(plugin, "__file__", repr(plugin)) 

237 content = "{}-{} at {}".format(dist.project_name, dist.version, loc) 

238 lines.append(" " + content) 

239 return lines 

240 

241 

242def pytest_report_header(config: Config) -> List[str]: 

243 lines = [] 

244 if config.option.debug or config.option.traceconfig: 

245 lines.append( 

246 "using: pytest-{} pylib-{}".format(pytest.__version__, py.__version__) 

247 ) 

248 

249 verinfo = getpluginversioninfo(config) 

250 if verinfo: 

251 lines.extend(verinfo) 

252 

253 if config.option.traceconfig: 

254 lines.append("active plugins:") 

255 items = config.pluginmanager.list_name_plugin() 

256 for name, plugin in items: 

257 if hasattr(plugin, "__file__"): 

258 r = plugin.__file__ 

259 else: 

260 r = repr(plugin) 

261 lines.append(" {:<20}: {}".format(name, r)) 

262 return lines