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 Unless disabled by ignore_cwd config or sys.flags.safe_path. 

251 

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

253 or `dist-packages` directory, 

254 so that it is after the standard library. 

255 

256 .. versionchanged:: 7.2 

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

258 .. versionchanged:: 8.0 

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

260 .. versionchanged:: 9.7 

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

262 """ 

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

264 return 

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

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

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

268 break 

269 else: 

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

271 # back to original behavior of inserting at the front 

272 idx = 0 

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

274 

275 def init_shell(self): 

276 raise NotImplementedError("Override in subclasses") 

277 

278 def init_gui_pylab(self): 

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

280 enable = False 

281 shell = self.shell 

282 if self.pylab: 

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

284 key = self.pylab 

285 elif self.matplotlib: 

286 enable = shell.enable_matplotlib 

287 key = self.matplotlib 

288 elif self.gui: 

289 enable = shell.enable_gui 

290 key = self.gui 

291 

292 if not enable: 

293 return 

294 

295 try: 

296 r = enable(key) 

297 except ImportError: 

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

299 self.shell.showtraceback() 

300 return 

301 except Exception: 

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

303 self.shell.showtraceback() 

304 return 

305 

306 if isinstance(r, tuple): 

307 gui, backend = r[:2] 

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

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

310 if key == "auto": 

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

312 else: 

313 gui = r 

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

315 "eventloop=%s", gui) 

316 

317 def init_extensions(self): 

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

319 

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

321 the extensions listed in ``self.extensions``. 

322 """ 

323 try: 

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

325 extensions = ( 

326 self.default_extensions + self.extensions + self.extra_extensions 

327 ) 

328 for ext in extensions: 

329 try: 

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

331 self.shell.extension_manager.load_extension(ext) 

332 except: 

333 if self.reraise_ipython_extension_failures: 

334 raise 

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

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

337 ext=ext, 

338 location=self.profile_dir.location 

339 )) 

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

341 except: 

342 if self.reraise_ipython_extension_failures: 

343 raise 

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

345 

346 def init_code(self): 

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

348 self._run_startup_files() 

349 self._run_exec_lines() 

350 self._run_exec_files() 

351 

352 # Hide variables defined here from %who etc. 

353 if self.hide_initial_ns: 

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

355 

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

357 # should *not* be excluded from %whos 

358 self._run_cmd_line_code() 

359 self._run_module() 

360 

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

362 sys.stdout.flush() 

363 sys.stderr.flush() 

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

365 

366 def _run_exec_lines(self): 

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

368 if not self.exec_lines: 

369 return 

370 try: 

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

372 for line in self.exec_lines: 

373 try: 

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

375 line) 

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

377 except: 

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

379 "namespace: %s" % line) 

380 self.shell.showtraceback() 

381 except: 

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

383 self.shell.showtraceback() 

384 

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

386 try: 

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

388 except IOError: 

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

390 return 

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

392 # were run from a system shell. 

393 save_argv = sys.argv 

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

395 try: 

396 if os.path.isfile(full_filename): 

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

398 full_filename) 

399 # Ensure that __file__ is always defined to match Python 

400 # behavior. 

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

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

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

404 self.shell.safe_execfile_ipy(full_filename, 

405 shell_futures=shell_futures) 

406 else: 

407 # default to python, even without extension 

408 self.shell.safe_execfile(full_filename, 

409 self.shell.user_ns, 

410 shell_futures=shell_futures, 

411 raise_exceptions=True) 

412 finally: 

413 sys.argv = save_argv 

414 

415 def _run_startup_files(self): 

416 """Run files from profile startup directory""" 

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

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

419 ] 

420 startup_files = [] 

421 

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

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

424 python_startup = os.environ['PYTHONSTARTUP'] 

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

426 try: 

427 self._exec_file(python_startup) 

428 except: 

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

430 self.shell.showtraceback() 

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

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

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

434 if not startup_files: 

435 return 

436 

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

438 try: 

439 for fname in sorted(startup_files): 

440 self._exec_file(fname) 

441 except: 

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

443 self.shell.showtraceback() 

444 

445 def _run_exec_files(self): 

446 """Run files from IPythonApp.exec_files""" 

447 if not self.exec_files: 

448 return 

449 

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

451 try: 

452 for fname in self.exec_files: 

453 self._exec_file(fname) 

454 except: 

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

456 self.shell.showtraceback() 

457 

458 def _run_cmd_line_code(self): 

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

460 if self.code_to_run: 

461 line = self.code_to_run 

462 try: 

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

464 line) 

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

466 except: 

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

468 line) 

469 self.shell.showtraceback() 

470 if not self.interact: 

471 self.exit(1) 

472 

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

474 elif self.file_to_run: 

475 fname = self.file_to_run 

476 if os.path.isdir(fname): 

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

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

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

480 if not self.interact: 

481 self.exit(2) 

482 try: 

483 self._exec_file(fname, shell_futures=True) 

484 except: 

485 self.shell.showtraceback(tb_offset=4) 

486 if not self.interact: 

487 self.exit(1) 

488 

489 def _run_module(self): 

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

491 if self.module_to_run: 

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

493 # run using `python -m`. 

494 save_argv = sys.argv 

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

496 try: 

497 self.shell.safe_run_module(self.module_to_run, 

498 self.shell.user_ns) 

499 finally: 

500 sys.argv = save_argv