Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/helpconfig.py: 31%

132 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

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 pytest 

10from _pytest.config import Config 

11from _pytest.config import ExitCode 

12from _pytest.config import PrintHelp 

13from _pytest.config.argparsing import Parser 

14 

15 

16class HelpAction(Action): 

17 """An argparse Action that will raise an exception in order to skip the 

18 rest of the argument parsing when --help is passed. 

19 

20 This prevents argparse from quitting due to missing required arguments 

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

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

23 implemented by raising SystemExit. 

24 """ 

25 

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

27 super().__init__( 

28 option_strings=option_strings, 

29 dest=dest, 

30 const=True, 

31 default=default, 

32 nargs=0, 

33 help=help, 

34 ) 

35 

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

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

38 

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

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

41 raise PrintHelp 

42 

43 

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

45 group = parser.getgroup("debugconfig") 

46 group.addoption( 

47 "--version", 

48 "-V", 

49 action="count", 

50 default=0, 

51 dest="version", 

52 help="Display pytest version and information about plugins. " 

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

54 ) 

55 group._addoption( 

56 "-h", 

57 "--help", 

58 action=HelpAction, 

59 dest="help", 

60 help="Show help message and configuration info", 

61 ) 

62 group._addoption( 

63 "-p", 

64 action="append", 

65 dest="plugins", 

66 default=[], 

67 metavar="name", 

68 help="Early-load given plugin module name or entry point (multi-allowed). " 

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

70 "`no:doctest`.", 

71 ) 

72 group.addoption( 

73 "--traceconfig", 

74 "--trace-config", 

75 action="store_true", 

76 default=False, 

77 help="Trace considerations of conftest.py files", 

78 ) 

79 group.addoption( 

80 "--debug", 

81 action="store", 

82 nargs="?", 

83 const="pytestdebug.log", 

84 dest="debug", 

85 metavar="DEBUG_FILE_NAME", 

86 help="Store internal tracing debug information in this log file. " 

87 "This file is opened with 'w' and truncated as a result, care advised. " 

88 "Default: pytestdebug.log.", 

89 ) 

90 group._addoption( 

91 "-o", 

92 "--override-ini", 

93 dest="override_ini", 

94 action="append", 

95 help='Override ini option with "option=value" style, ' 

96 "e.g. `-o xfail_strict=True -o cache_dir=cache`.", 

97 ) 

98 

99 

100@pytest.hookimpl(hookwrapper=True) 

101def pytest_cmdline_parse(): 

102 outcome = yield 

103 config: Config = outcome.get_result() 

104 

105 if config.option.debug: 

106 # --debug | --debug <file.log> was provided. 

107 path = config.option.debug 

108 debugfile = open(path, "w") 

109 debugfile.write( 

110 "versions pytest-%s, " 

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

112 % ( 

113 pytest.__version__, 

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

115 os.getcwd(), 

116 config.invocation_params.args, 

117 ) 

118 ) 

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

120 undo_tracing = config.pluginmanager.enable_tracing() 

121 sys.stderr.write("writing pytest debug information to %s\n" % path) 

122 

123 def unset_tracing() -> None: 

124 debugfile.close() 

125 sys.stderr.write("wrote pytest debug information to %s\n" % debugfile.name) 

126 config.trace.root.setwriter(None) 

127 undo_tracing() 

128 

129 config.add_cleanup(unset_tracing) 

130 

131 

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

133 if config.option.version > 1: 

134 sys.stdout.write( 

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

136 pytest.__version__, pytest.__file__ 

137 ) 

138 ) 

139 plugininfo = getpluginversioninfo(config) 

140 if plugininfo: 

141 for line in plugininfo: 

142 sys.stdout.write(line + "\n") 

143 else: 

144 sys.stdout.write(f"pytest {pytest.__version__}\n") 

145 

146 

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

148 if config.option.version > 0: 

149 showversion(config) 

150 return 0 

151 elif config.option.help: 

152 config._do_configure() 

153 showhelp(config) 

154 config._ensure_unconfigure() 

155 return 0 

156 return None 

157 

158 

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

160 import textwrap 

161 

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

163 tw = reporter._tw 

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

165 tw.line() 

166 tw.line( 

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

168 ) 

169 tw.line() 

170 

171 columns = tw.fullwidth # costly call 

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

173 indent = " " * indent_len 

174 for name in config._parser._ininames: 

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

176 if type is None: 

177 type = "string" 

178 if help is None: 

179 raise TypeError(f"help argument cannot be None for {name}") 

180 spec = f"{name} ({type}):" 

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

182 spec_len = len(spec) 

183 if spec_len > (indent_len - 3): 

184 # Display help starting at a new line. 

185 tw.line() 

186 helplines = textwrap.wrap( 

187 help, 

188 columns, 

189 initial_indent=indent, 

190 subsequent_indent=indent, 

191 break_on_hyphens=False, 

192 ) 

193 

194 for line in helplines: 

195 tw.line(line) 

196 else: 

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

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

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

200 

201 if wrapped: 

202 tw.line(wrapped[0]) 

203 for line in wrapped[1:]: 

204 tw.line(indent + line) 

205 

206 tw.line() 

207 tw.line("Environment variables:") 

208 vars = [ 

209 ("PYTEST_ADDOPTS", "Extra command line options"), 

210 ("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"), 

211 ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"), 

212 ("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"), 

213 ] 

214 for name, help in vars: 

215 tw.line(f" {name:<24} {help}") 

216 tw.line() 

217 tw.line() 

218 

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

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

221 tw.line( 

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

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

224 "with the '-v' option" 

225 ) 

226 

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

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

229 return 

230 

231 

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

233 

234 

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

236 lines = [] 

237 plugininfo = config.pluginmanager.list_plugin_distinfo() 

238 if plugininfo: 

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

240 for plugin, dist in plugininfo: 

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

242 content = f"{dist.project_name}-{dist.version} at {loc}" 

243 lines.append(" " + content) 

244 return lines 

245 

246 

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

248 lines = [] 

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

250 lines.append(f"using: pytest-{pytest.__version__}") 

251 

252 verinfo = getpluginversioninfo(config) 

253 if verinfo: 

254 lines.extend(verinfo) 

255 

256 if config.option.traceconfig: 

257 lines.append("active plugins:") 

258 items = config.pluginmanager.list_name_plugin() 

259 for name, plugin in items: 

260 if hasattr(plugin, "__file__"): 

261 r = plugin.__file__ 

262 else: 

263 r = repr(plugin) 

264 lines.append(f" {name:<20}: {r}") 

265 return lines