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

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# encoding: utf-8 

2""" 

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

4launch InteractiveShell instances, load extensions, etc. 

5""" 

6 

7# Copyright (c) IPython Development Team. 

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

9 

10import glob 

11from itertools import chain 

12import os 

13import sys 

14import typing as t 

15 

16from traitlets.config.application import boolean_flag 

17from traitlets.config.configurable import Configurable 

18from traitlets.config.loader import Config 

19from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS 

20from IPython.utils.contexts import preserve_keys 

21from IPython.utils.path import filefind 

22from traitlets import ( 

23 Unicode, 

24 Instance, 

25 List, 

26 Bool, 

27 CaselessStrEnum, 

28 observe, 

29 DottedObjectName, 

30 Undefined, 

31) 

32from IPython.terminal import pt_inputhooks 

33 

34# ----------------------------------------------------------------------------- 

35# Aliases and Flags 

36# ----------------------------------------------------------------------------- 

37 

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

39 

40shell_flags = {} 

41 

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

43addflag( 

44 "autoindent", 

45 "InteractiveShell.autoindent", 

46 "Turn on autoindenting.", 

47 "Turn off autoindenting.", 

48) 

49addflag( 

50 "automagic", 

51 "InteractiveShell.automagic", 

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

53 IPython prompt for more information.""", 

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

55) 

56addflag('pdb', 'InteractiveShell.pdb', 

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

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

59) 

60addflag('pprint', 'PlainTextFormatter.pprint', 

61 "Enable auto pretty printing of results.", 

62 "Disable auto pretty printing of results." 

63) 

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

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

66 and optionally can use colors for this, syntax highlighting 

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

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

69 colours.""", 

70 "Disable using colors for info related things." 

71) 

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

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

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

75) 

76nosep_config = Config() 

77nosep_config.InteractiveShell.separate_in = '' 

78nosep_config.InteractiveShell.separate_out = '' 

79nosep_config.InteractiveShell.separate_out2 = '' 

80 

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

82shell_flags['pylab'] = ( 

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

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

85 the default matplotlib backend. The exact options available 

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

87) 

88shell_flags['matplotlib'] = ( 

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

90 """Configure matplotlib for interactive use with 

91 the default matplotlib backend. The exact options available 

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

93) 

94 

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

96shell_aliases = dict( 

97 autocall="InteractiveShell.autocall", 

98 colors="InteractiveShell.colors", 

99 theme="InteractiveShell.colors", 

100 logfile="InteractiveShell.logfile", 

101 logappend="InteractiveShell.logappend", 

102 c="InteractiveShellApp.code_to_run", 

103 m="InteractiveShellApp.module_to_run", 

104 ext="InteractiveShellApp.extra_extensions", 

105 gui='InteractiveShellApp.gui', 

106 pylab='InteractiveShellApp.pylab', 

107 matplotlib='InteractiveShellApp.matplotlib', 

108) 

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

110 

111 

112# ----------------------------------------------------------------------------- 

113# Traitlets 

114# ----------------------------------------------------------------------------- 

115 

116 

117class MatplotlibBackendCaselessStrEnum(CaselessStrEnum): 

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

119 

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

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

122 

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

124 wasting unnecessary initialisation time. 

125 """ 

126 

127 def __init__( 

128 self: CaselessStrEnum[t.Any], 

129 default_value: t.Any = Undefined, 

130 **kwargs: t.Any, 

131 ) -> None: 

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

133 

134 def __getattribute__(self, name): 

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

136 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops 

137 

138 self.values = _list_matplotlib_backends_and_gui_loops() 

139 return object.__getattribute__(self, name) 

140 

141 

142#----------------------------------------------------------------------------- 

143# Main classes and functions 

144#----------------------------------------------------------------------------- 

145 

146class InteractiveShellApp(Configurable): 

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

148 

149 Provides configurables for loading extensions and executing files 

150 as part of configuring a Shell environment. 

151 

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

153 of the subclass: 

154 

155 - :meth:`init_path` 

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

157 - :meth:`init_gui_pylab` 

158 - :meth:`init_extensions` 

159 - :meth:`init_code` 

160 """ 

161 extensions = List(Unicode(), 

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

163 ).tag(config=True) 

164 

165 extra_extensions = List( 

166 DottedObjectName(), 

167 help=""" 

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

169 

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

171 

172 .. versionadded:: 7.10 

173 """, 

174 ).tag(config=True) 

175 

176 reraise_ipython_extension_failures = Bool(False, 

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

178 ).tag(config=True) 

179 

180 # Extensions that are always loaded (not configurable) 

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

182 

183 hide_initial_ns = Bool(True, 

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

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

186 ).tag(config=True) 

187 

188 exec_files = List(Unicode(), 

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

190 ).tag(config=True) 

191 exec_PYTHONSTARTUP = Bool(True, 

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

193 variable at IPython startup.""" 

194 ).tag(config=True) 

195 file_to_run = Unicode('', 

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

197 

198 exec_lines = List(Unicode(), 

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

200 ).tag(config=True) 

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

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

203 gui = CaselessStrEnum( 

204 gui_keys, 

205 allow_none=True, 

206 help="Enable GUI event loop integration with any of {0}.".format(gui_keys), 

207 ).tag(config=True) 

208 matplotlib = MatplotlibBackendCaselessStrEnum( 

209 allow_none=True, 

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

211 the default matplotlib backend. The exact options available 

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

213 ).tag(config=True) 

214 pylab = MatplotlibBackendCaselessStrEnum( 

215 allow_none=True, 

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

217 selecting a particular matplotlib backend and loop integration. 

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

219 """, 

220 ).tag(config=True) 

221 pylab_import_all = Bool( 

222 True, 

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

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

225 

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

227 """, 

228 ).tag(config=True) 

229 ignore_cwd = Bool( 

230 False, 

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

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

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

234 ).tag(config=True) 

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

236 allow_none=True) 

237 # whether interact-loop should start 

238 interact = Bool(True) 

239 

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

241 @observe('user_ns') 

242 def _user_ns_changed(self, change): 

243 if self.shell is not None: 

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

245 self.shell.init_user_ns() 

246 

247 def init_path(self): 

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

249 

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

251 or `dist-packages` directory, 

252 so that it is after the standard library. 

253 

254 .. versionchanged:: 7.2 

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

256 .. versionchanged:: 8.0 

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

258 """ 

259 if '' in sys.path or self.ignore_cwd: 

260 return 

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

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

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

264 break 

265 else: 

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

267 # back to original behavior of inserting at the front 

268 idx = 0 

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

270 

271 def init_shell(self): 

272 raise NotImplementedError("Override in subclasses") 

273 

274 def init_gui_pylab(self): 

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

276 enable = False 

277 shell = self.shell 

278 if self.pylab: 

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

280 key = self.pylab 

281 elif self.matplotlib: 

282 enable = shell.enable_matplotlib 

283 key = self.matplotlib 

284 elif self.gui: 

285 enable = shell.enable_gui 

286 key = self.gui 

287 

288 if not enable: 

289 return 

290 

291 try: 

292 r = enable(key) 

293 except ImportError: 

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

295 self.shell.showtraceback() 

296 return 

297 except Exception: 

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

299 self.shell.showtraceback() 

300 return 

301 

302 if isinstance(r, tuple): 

303 gui, backend = r[:2] 

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

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

306 if key == "auto": 

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

308 else: 

309 gui = r 

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

311 "eventloop=%s", gui) 

312 

313 def init_extensions(self): 

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

315 

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

317 the extensions listed in ``self.extensions``. 

318 """ 

319 try: 

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

321 extensions = ( 

322 self.default_extensions + self.extensions + self.extra_extensions 

323 ) 

324 for ext in extensions: 

325 try: 

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

327 self.shell.extension_manager.load_extension(ext) 

328 except: 

329 if self.reraise_ipython_extension_failures: 

330 raise 

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

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

333 ext=ext, 

334 location=self.profile_dir.location 

335 )) 

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

337 except: 

338 if self.reraise_ipython_extension_failures: 

339 raise 

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

341 

342 def init_code(self): 

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

344 self._run_startup_files() 

345 self._run_exec_lines() 

346 self._run_exec_files() 

347 

348 # Hide variables defined here from %who etc. 

349 if self.hide_initial_ns: 

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

351 

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

353 # should *not* be excluded from %whos 

354 self._run_cmd_line_code() 

355 self._run_module() 

356 

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

358 sys.stdout.flush() 

359 sys.stderr.flush() 

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

361 

362 def _run_exec_lines(self): 

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

364 if not self.exec_lines: 

365 return 

366 try: 

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

368 for line in self.exec_lines: 

369 try: 

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

371 line) 

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

373 except: 

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

375 "namespace: %s" % line) 

376 self.shell.showtraceback() 

377 except: 

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

379 self.shell.showtraceback() 

380 

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

382 try: 

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

384 except IOError: 

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

386 return 

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

388 # were run from a system shell. 

389 save_argv = sys.argv 

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

391 try: 

392 if os.path.isfile(full_filename): 

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

394 full_filename) 

395 # Ensure that __file__ is always defined to match Python 

396 # behavior. 

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

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

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

400 self.shell.safe_execfile_ipy(full_filename, 

401 shell_futures=shell_futures) 

402 else: 

403 # default to python, even without extension 

404 self.shell.safe_execfile(full_filename, 

405 self.shell.user_ns, 

406 shell_futures=shell_futures, 

407 raise_exceptions=True) 

408 finally: 

409 sys.argv = save_argv 

410 

411 def _run_startup_files(self): 

412 """Run files from profile startup directory""" 

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

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

415 ] 

416 startup_files = [] 

417 

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

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

420 python_startup = os.environ['PYTHONSTARTUP'] 

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

422 try: 

423 self._exec_file(python_startup) 

424 except: 

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

426 self.shell.showtraceback() 

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

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

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

430 if not startup_files: 

431 return 

432 

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

434 try: 

435 for fname in sorted(startup_files): 

436 self._exec_file(fname) 

437 except: 

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

439 self.shell.showtraceback() 

440 

441 def _run_exec_files(self): 

442 """Run files from IPythonApp.exec_files""" 

443 if not self.exec_files: 

444 return 

445 

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

447 try: 

448 for fname in self.exec_files: 

449 self._exec_file(fname) 

450 except: 

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

452 self.shell.showtraceback() 

453 

454 def _run_cmd_line_code(self): 

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

456 if self.code_to_run: 

457 line = self.code_to_run 

458 try: 

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

460 line) 

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

462 except: 

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

464 line) 

465 self.shell.showtraceback() 

466 if not self.interact: 

467 self.exit(1) 

468 

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

470 elif self.file_to_run: 

471 fname = self.file_to_run 

472 if os.path.isdir(fname): 

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

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

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

476 if not self.interact: 

477 self.exit(2) 

478 try: 

479 self._exec_file(fname, shell_futures=True) 

480 except: 

481 self.shell.showtraceback(tb_offset=4) 

482 if not self.interact: 

483 self.exit(1) 

484 

485 def _run_module(self): 

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

487 if self.module_to_run: 

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

489 # run using `python -m`. 

490 save_argv = sys.argv 

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

492 try: 

493 self.shell.safe_run_module(self.module_to_run, 

494 self.shell.user_ns) 

495 finally: 

496 sys.argv = save_argv