Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/shellapp.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

233 statements  

1""" 

2A mixin for :class:`~IPython.core.application.Application` classes that 

3launch InteractiveShell instances, load extensions, etc. 

4""" 

5 

6# Copyright (c) IPython Development Team. 

7# Distributed under the terms of the Modified BSD License. 

8 

9from __future__ import annotations 

10 

11import glob 

12from itertools import chain 

13import os 

14import sys 

15from typing import Any 

16 

17from traitlets.config.application import boolean_flag 

18from traitlets.config.configurable import Configurable 

19from traitlets.config.loader import Config 

20from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS 

21from IPython.utils.contexts import preserve_keys 

22from IPython.utils.path import filefind 

23from traitlets import ( 

24 Unicode, 

25 Instance, 

26 List, 

27 Bool, 

28 CaselessStrEnum, 

29 observe, 

30 DottedObjectName, 

31 Undefined, 

32) 

33from IPython.terminal import pt_inputhooks 

34 

35# ----------------------------------------------------------------------------- 

36# Aliases and Flags 

37# ----------------------------------------------------------------------------- 

38 

39gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases)) 

40 

41shell_flags = {} 

42 

43addflag = lambda *args: shell_flags.update(boolean_flag(*args)) 

44addflag( 

45 "autoindent", 

46 "InteractiveShell.autoindent", 

47 "Turn on autoindenting.", 

48 "Turn off autoindenting.", 

49) 

50addflag( 

51 "automagic", 

52 "InteractiveShell.automagic", 

53 """Turn on the auto calling of magic commands. Type %%magic at the 

54 IPython prompt for more information.""", 

55 'Turn off the auto calling of magic commands.' 

56) 

57addflag('pdb', 'InteractiveShell.pdb', 

58 "Enable auto calling the pdb debugger after every exception.", 

59 "Disable auto calling the pdb debugger after every exception." 

60) 

61addflag('pprint', 'PlainTextFormatter.pprint', 

62 "Enable auto pretty printing of results.", 

63 "Disable auto pretty printing of results." 

64) 

65addflag('color-info', 'InteractiveShell.color_info', 

66 """IPython can display information about objects via a set of functions, 

67 and optionally can use colors for this, syntax highlighting 

68 source code and various other elements. This is on by default, but can cause 

69 problems with some pagers. If you see such problems, you can disable the 

70 colours.""", 

71 "Disable using colors for info related things." 

72) 

73addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd', 

74 "Exclude the current working directory from sys.path", 

75 "Include the current working directory in sys.path", 

76) 

77nosep_config = Config() 

78nosep_config.InteractiveShell.separate_in = '' 

79nosep_config.InteractiveShell.separate_out = '' 

80nosep_config.InteractiveShell.separate_out2 = '' 

81 

82shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.") 

83shell_flags['pylab'] = ( 

84 {'InteractiveShellApp' : {'pylab' : 'auto'}}, 

85 """Pre-load matplotlib and numpy for interactive use with 

86 the default matplotlib backend. The exact options available 

87 depend on what Matplotlib provides at runtime.""", 

88) 

89shell_flags['matplotlib'] = ( 

90 {'InteractiveShellApp' : {'matplotlib' : 'auto'}}, 

91 """Configure matplotlib for interactive use with 

92 the default matplotlib backend. The exact options available 

93 depend on what Matplotlib provides at runtime.""", 

94) 

95 

96# it's possible we don't want short aliases for *all* of these: 

97shell_aliases = dict( 

98 autocall="InteractiveShell.autocall", 

99 colors="InteractiveShell.colors", 

100 theme="InteractiveShell.colors", 

101 logfile="InteractiveShell.logfile", 

102 logappend="InteractiveShell.logappend", 

103 c="InteractiveShellApp.code_to_run", 

104 m="InteractiveShellApp.module_to_run", 

105 ext="InteractiveShellApp.extra_extensions", 

106 gui='InteractiveShellApp.gui', 

107 pylab='InteractiveShellApp.pylab', 

108 matplotlib='InteractiveShellApp.matplotlib', 

109) 

110shell_aliases['cache-size'] = 'InteractiveShell.cache_size' 

111 

112 

113# ----------------------------------------------------------------------------- 

114# Traitlets 

115# ----------------------------------------------------------------------------- 

116 

117 

118class MatplotlibBackendCaselessStrEnum(CaselessStrEnum): 

119 """An enum of Matplotlib backend strings where the case should be ignored. 

120 

121 Prior to Matplotlib 3.9.0 the list of valid backends is hardcoded in 

122 pylabtools.backends. After that, Matplotlib manages backends. 

123 

124 The list of valid backends is determined when it is first needed to avoid 

125 wasting unnecessary initialisation time. 

126 """ 

127 

128 def __init__( 

129 self: CaselessStrEnum[Any], 

130 default_value: Any = Undefined, 

131 **kwargs: Any, 

132 ) -> None: 

133 super().__init__(None, default_value=default_value, **kwargs) 

134 

135 def __getattribute__(self, name): 

136 if name == "values" and object.__getattribute__(self, name) is None: 

137 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops 

138 

139 self.values = _list_matplotlib_backends_and_gui_loops() 

140 return object.__getattribute__(self, name) 

141 

142 

143#----------------------------------------------------------------------------- 

144# Main classes and functions 

145#----------------------------------------------------------------------------- 

146 

147class InteractiveShellApp(Configurable): 

148 """A Mixin for applications that start InteractiveShell instances. 

149 

150 Provides configurables for loading extensions and executing files 

151 as part of configuring a Shell environment. 

152 

153 The following methods should be called by the :meth:`initialize` method 

154 of the subclass: 

155 

156 - :meth:`init_path` 

157 - :meth:`init_shell` (to be implemented by the subclass) 

158 - :meth:`init_gui_pylab` 

159 - :meth:`init_extensions` 

160 - :meth:`init_code` 

161 """ 

162 extensions = List(Unicode(), 

163 help="A list of dotted module names of IPython extensions to load." 

164 ).tag(config=True) 

165 

166 extra_extensions = List( 

167 DottedObjectName(), 

168 help=""" 

169 Dotted module name(s) of one or more IPython extensions to load. 

170 

171 For specifying extra extensions to load on the command-line. 

172 

173 .. versionadded:: 7.10 

174 """, 

175 ).tag(config=True) 

176 

177 reraise_ipython_extension_failures = Bool(False, 

178 help="Reraise exceptions encountered loading IPython extensions?", 

179 ).tag(config=True) 

180 

181 # Extensions that are always loaded (not configurable) 

182 default_extensions = List(Unicode(), ['storemagic']).tag(config=False) 

183 

184 hide_initial_ns = Bool(True, 

185 help="""Should variables loaded at startup (by startup files, exec_lines, etc.) 

186 be hidden from tools like %who?""" 

187 ).tag(config=True) 

188 

189 exec_files = List(Unicode(), 

190 help="""List of files to run at IPython startup.""" 

191 ).tag(config=True) 

192 exec_PYTHONSTARTUP = Bool(True, 

193 help="""Run the file referenced by the PYTHONSTARTUP environment 

194 variable at IPython startup.""" 

195 ).tag(config=True) 

196 file_to_run = Unicode('', 

197 help="""A file to be run""").tag(config=True) 

198 

199 exec_lines = List(Unicode(), 

200 help="""lines of code to run at IPython startup.""" 

201 ).tag(config=True) 

202 code_to_run = Unicode("", help="Execute the given command string.").tag(config=True) 

203 module_to_run = Unicode("", help="Run the module as a script.").tag(config=True) 

204 gui = CaselessStrEnum( 

205 gui_keys, 

206 allow_none=True, 

207 help=f"Enable GUI event loop integration with any of {gui_keys}.", 

208 ).tag(config=True) 

209 matplotlib = MatplotlibBackendCaselessStrEnum( 

210 allow_none=True, 

211 help="""Configure matplotlib for interactive use with 

212 the default matplotlib backend. The exact options available 

213 depend on what Matplotlib provides at runtime.""", 

214 ).tag(config=True) 

215 pylab = MatplotlibBackendCaselessStrEnum( 

216 allow_none=True, 

217 help="""Pre-load matplotlib and numpy for interactive use, 

218 selecting a particular matplotlib backend and loop integration. 

219 The exact options available depend on what Matplotlib provides at runtime. 

220 """, 

221 ).tag(config=True) 

222 pylab_import_all = Bool( 

223 True, 

224 help="""If true, IPython will populate the user namespace with numpy, pylab, etc. 

225 and an ``import *`` is done from numpy and pylab, when using pylab mode. 

226 

227 When False, pylab mode should not import any names into the user namespace. 

228 """, 

229 ).tag(config=True) 

230 ignore_cwd = Bool( 

231 False, 

232 help="""If True, IPython will not add the current working directory to sys.path. 

233 When False, the current working directory is added to sys.path, allowing imports 

234 of modules defined in the current directory.""" 

235 ).tag(config=True) 

236 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', 

237 allow_none=True) 

238 # whether interact-loop should start 

239 interact = Bool(True) 

240 

241 user_ns = Instance(dict, args=None, allow_none=True) 

242 @observe('user_ns') 

243 def _user_ns_changed(self, change): 

244 if self.shell is not None: 

245 self.shell.user_ns = change['new'] 

246 self.shell.init_user_ns() 

247 

248 def init_path(self): 

249 """Add current working directory, '', to sys.path 

250 

251 Unless disabled by ignore_cwd config or sys.flags.safe_path. 

252 

253 Unlike Python's default, we insert before the first `site-packages` 

254 or `dist-packages` directory, 

255 so that it is after the standard library. 

256 

257 .. versionchanged:: 7.2 

258 Try to insert after the standard library, instead of first. 

259 .. versionchanged:: 8.0 

260 Allow optionally not including the current directory in sys.path 

261 .. versionchanged:: 9.7 

262 Respect sys.flags.safe_path (PYTHONSAFEPATH and -P flag) 

263 """ 

264 if "" in sys.path or self.ignore_cwd or sys.flags.safe_path: 

265 return 

266 for idx, path in enumerate(sys.path): 

267 parent, last_part = os.path.split(path) 

268 if last_part in {'site-packages', 'dist-packages'}: 

269 break 

270 else: 

271 # no site-packages or dist-packages found (?!) 

272 # back to original behavior of inserting at the front 

273 idx = 0 

274 sys.path.insert(idx, '') 

275 

276 def init_shell(self): 

277 raise NotImplementedError("Override in subclasses") 

278 

279 def init_gui_pylab(self): 

280 """Enable GUI event loop integration, taking pylab into account.""" 

281 enable = False 

282 shell = self.shell 

283 if self.pylab: 

284 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all) 

285 key = self.pylab 

286 elif self.matplotlib: 

287 enable = shell.enable_matplotlib 

288 key = self.matplotlib 

289 elif self.gui: 

290 enable = shell.enable_gui 

291 key = self.gui 

292 

293 if not enable: 

294 return 

295 

296 try: 

297 r = enable(key) 

298 except ImportError: 

299 self.log.warning("Eventloop or matplotlib integration failed. Is matplotlib installed?") 

300 self.shell.showtraceback() 

301 return 

302 except Exception: 

303 self.log.warning("GUI event loop or pylab initialization failed") 

304 self.shell.showtraceback() 

305 return 

306 

307 if isinstance(r, tuple): 

308 gui, backend = r[:2] 

309 self.log.info("Enabling GUI event loop integration, " 

310 "eventloop=%s, matplotlib=%s", gui, backend) 

311 if key == "auto": 

312 print("Using matplotlib backend: %s" % backend) 

313 else: 

314 gui = r 

315 self.log.info("Enabling GUI event loop integration, " 

316 "eventloop=%s", gui) 

317 

318 def init_extensions(self): 

319 """Load all IPython extensions in IPythonApp.extensions. 

320 

321 This uses the :meth:`ExtensionManager.load_extensions` to load all 

322 the extensions listed in ``self.extensions``. 

323 """ 

324 try: 

325 self.log.debug("Loading IPython extensions...") 

326 extensions = ( 

327 self.default_extensions + self.extensions + self.extra_extensions 

328 ) 

329 for ext in extensions: 

330 try: 

331 self.log.info("Loading IPython extension: %s", ext) 

332 self.shell.extension_manager.load_extension(ext) 

333 except Exception: 

334 if self.reraise_ipython_extension_failures: 

335 raise 

336 msg = ("Error in loading extension: {ext}\n" 

337 "Check your config files in {location}".format( 

338 ext=ext, 

339 location=self.profile_dir.location 

340 )) 

341 self.log.warning(msg, exc_info=True) 

342 except Exception: 

343 if self.reraise_ipython_extension_failures: 

344 raise 

345 self.log.warning("Unknown error in loading extensions:", exc_info=True) 

346 

347 def init_code(self): 

348 """run the pre-flight code, specified via exec_lines""" 

349 self._run_startup_files() 

350 self._run_exec_lines() 

351 self._run_exec_files() 

352 

353 # Hide variables defined here from %who etc. 

354 if self.hide_initial_ns: 

355 self.shell.user_ns_hidden.update(self.shell.user_ns) 

356 

357 # command-line execution (ipython -i script.py, ipython -m module) 

358 # should *not* be excluded from %whos 

359 self._run_cmd_line_code() 

360 self._run_module() 

361 

362 # flush output, so itwon't be attached to the first cell 

363 sys.stdout.flush() 

364 sys.stderr.flush() 

365 self.shell._sys_modules_keys = set(sys.modules.keys()) 

366 

367 def _run_exec_lines(self): 

368 """Run lines of code in IPythonApp.exec_lines in the user's namespace.""" 

369 if not self.exec_lines: 

370 return 

371 try: 

372 self.log.debug("Running code from IPythonApp.exec_lines...") 

373 for line in self.exec_lines: 

374 try: 

375 self.log.info("Running code in user namespace: %s" % 

376 line) 

377 self.shell.run_cell(line, store_history=False) 

378 except Exception: 

379 self.log.warning("Error in executing line in user " 

380 "namespace: %s" % line) 

381 self.shell.showtraceback() 

382 except Exception: 

383 self.log.warning("Unknown error in handling IPythonApp.exec_lines:") 

384 self.shell.showtraceback() 

385 

386 def _exec_file(self, fname, shell_futures=False): 

387 try: 

388 full_filename = filefind(fname, ['.', self.ipython_dir]) 

389 except OSError: 

390 self.log.warning("File not found: %r"%fname) 

391 return 

392 # Make sure that the running script gets a proper sys.argv as if it 

393 # were run from a system shell. 

394 save_argv = sys.argv 

395 sys.argv = [full_filename] + self.extra_args[1:] 

396 try: 

397 if os.path.isfile(full_filename): 

398 self.log.info("Running file in user namespace: %s" % 

399 full_filename) 

400 # Ensure that __file__ is always defined to match Python 

401 # behavior. 

402 with preserve_keys(self.shell.user_ns, '__file__'): 

403 self.shell.user_ns['__file__'] = fname 

404 if full_filename.endswith('.ipy') or full_filename.endswith('.ipynb'): 

405 self.shell.safe_execfile_ipy(full_filename, 

406 shell_futures=shell_futures) 

407 else: 

408 # default to python, even without extension 

409 self.shell.safe_execfile(full_filename, 

410 self.shell.user_ns, 

411 shell_futures=shell_futures, 

412 raise_exceptions=True) 

413 finally: 

414 sys.argv = save_argv 

415 

416 def _run_startup_files(self): 

417 """Run files from profile startup directory""" 

418 startup_dirs = [self.profile_dir.startup_dir] + [ 

419 os.path.join(p, 'startup') for p in chain(ENV_CONFIG_DIRS, SYSTEM_CONFIG_DIRS) 

420 ] 

421 startup_files = [] 

422 

423 if self.exec_PYTHONSTARTUP and os.environ.get('PYTHONSTARTUP', False) and \ 

424 not (self.file_to_run or self.code_to_run or self.module_to_run): 

425 python_startup = os.environ['PYTHONSTARTUP'] 

426 self.log.debug("Running PYTHONSTARTUP file %s...", python_startup) 

427 try: 

428 self._exec_file(python_startup) 

429 except Exception: 

430 self.log.warning("Unknown error in handling PYTHONSTARTUP file %s:", python_startup) 

431 self.shell.showtraceback() 

432 for startup_dir in startup_dirs[::-1]: 

433 startup_files += glob.glob(os.path.join(startup_dir, '*.py')) 

434 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy')) 

435 if not startup_files: 

436 return 

437 

438 self.log.debug("Running startup files from %s...", startup_dir) 

439 try: 

440 for fname in sorted(startup_files): 

441 self._exec_file(fname) 

442 except Exception: 

443 self.log.warning("Unknown error in handling startup files:") 

444 self.shell.showtraceback() 

445 

446 def _run_exec_files(self): 

447 """Run files from IPythonApp.exec_files""" 

448 if not self.exec_files: 

449 return 

450 

451 self.log.debug("Running files in IPythonApp.exec_files...") 

452 try: 

453 for fname in self.exec_files: 

454 self._exec_file(fname) 

455 except Exception: 

456 self.log.warning("Unknown error in handling IPythonApp.exec_files:") 

457 self.shell.showtraceback() 

458 

459 def _run_cmd_line_code(self): 

460 """Run code or file specified at the command-line""" 

461 if self.code_to_run: 

462 line = self.code_to_run 

463 try: 

464 self.log.info("Running code given at command line (c=): %s" % 

465 line) 

466 self.shell.run_cell(line, store_history=False) 

467 except Exception: 

468 self.log.warning("Error in executing line in user namespace: %s" % 

469 line) 

470 self.shell.showtraceback() 

471 if not self.interact: 

472 self.exit(1) 

473 

474 # Like Python itself, ignore the second if the first of these is present 

475 elif self.file_to_run: 

476 fname = self.file_to_run 

477 if os.path.isdir(fname): 

478 fname = os.path.join(fname, "__main__.py") 

479 if not os.path.exists(fname): 

480 self.log.warning("File '%s' doesn't exist", fname) 

481 if not self.interact: 

482 self.exit(2) 

483 try: 

484 self._exec_file(fname, shell_futures=True) 

485 except Exception: 

486 self.shell.showtraceback(tb_offset=4) 

487 if not self.interact: 

488 self.exit(1) 

489 

490 def _run_module(self): 

491 """Run module specified at the command-line.""" 

492 if self.module_to_run: 

493 # Make sure that the module gets a proper sys.argv as if it were 

494 # run using `python -m`. 

495 save_argv = sys.argv 

496 sys.argv = [sys.executable] + self.extra_args 

497 try: 

498 self.shell.safe_run_module(self.module_to_run, 

499 self.shell.user_ns) 

500 finally: 

501 sys.argv = save_argv