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

226 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

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 

14 

15from traitlets.config.application import boolean_flag 

16from traitlets.config.configurable import Configurable 

17from traitlets.config.loader import Config 

18from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS 

19from IPython.core import pylabtools 

20from IPython.utils.contexts import preserve_keys 

21from IPython.utils.path import filefind 

22from traitlets import ( 

23 Unicode, Instance, List, Bool, CaselessStrEnum, observe, 

24 DottedObjectName, 

25) 

26from IPython.terminal import pt_inputhooks 

27 

28#----------------------------------------------------------------------------- 

29# Aliases and Flags 

30#----------------------------------------------------------------------------- 

31 

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

33 

34backend_keys = sorted(pylabtools.backends.keys()) 

35backend_keys.insert(0, 'auto') 

36 

37shell_flags = {} 

38 

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

40addflag('autoindent', 'InteractiveShell.autoindent', 

41 'Turn on autoindenting.', 'Turn off autoindenting.' 

42) 

43addflag('automagic', 'InteractiveShell.automagic', 

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

45 IPython prompt for more information.""", 

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

47) 

48addflag('pdb', 'InteractiveShell.pdb', 

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

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

51) 

52addflag('pprint', 'PlainTextFormatter.pprint', 

53 "Enable auto pretty printing of results.", 

54 "Disable auto pretty printing of results." 

55) 

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

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

58 and optionally can use colors for this, syntax highlighting 

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

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

61 colours.""", 

62 "Disable using colors for info related things." 

63) 

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

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

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

67) 

68nosep_config = Config() 

69nosep_config.InteractiveShell.separate_in = '' 

70nosep_config.InteractiveShell.separate_out = '' 

71nosep_config.InteractiveShell.separate_out2 = '' 

72 

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

74shell_flags['pylab'] = ( 

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

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

77 the default matplotlib backend.""" 

78) 

79shell_flags['matplotlib'] = ( 

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

81 """Configure matplotlib for interactive use with 

82 the default matplotlib backend.""" 

83) 

84 

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

86shell_aliases = dict( 

87 autocall='InteractiveShell.autocall', 

88 colors='InteractiveShell.colors', 

89 logfile='InteractiveShell.logfile', 

90 logappend='InteractiveShell.logappend', 

91 c='InteractiveShellApp.code_to_run', 

92 m='InteractiveShellApp.module_to_run', 

93 ext="InteractiveShellApp.extra_extensions", 

94 gui='InteractiveShellApp.gui', 

95 pylab='InteractiveShellApp.pylab', 

96 matplotlib='InteractiveShellApp.matplotlib', 

97) 

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

99 

100#----------------------------------------------------------------------------- 

101# Main classes and functions 

102#----------------------------------------------------------------------------- 

103 

104class InteractiveShellApp(Configurable): 

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

106 

107 Provides configurables for loading extensions and executing files 

108 as part of configuring a Shell environment. 

109 

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

111 of the subclass: 

112 

113 - :meth:`init_path` 

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

115 - :meth:`init_gui_pylab` 

116 - :meth:`init_extensions` 

117 - :meth:`init_code` 

118 """ 

119 extensions = List(Unicode(), 

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

121 ).tag(config=True) 

122 

123 extra_extensions = List( 

124 DottedObjectName(), 

125 help=""" 

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

127 

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

129 

130 .. versionadded:: 7.10 

131 """, 

132 ).tag(config=True) 

133 

134 reraise_ipython_extension_failures = Bool(False, 

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

136 ).tag(config=True) 

137 

138 # Extensions that are always loaded (not configurable) 

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

140 

141 hide_initial_ns = Bool(True, 

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

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

144 ).tag(config=True) 

145 

146 exec_files = List(Unicode(), 

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

148 ).tag(config=True) 

149 exec_PYTHONSTARTUP = Bool(True, 

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

151 variable at IPython startup.""" 

152 ).tag(config=True) 

153 file_to_run = Unicode('', 

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

155 

156 exec_lines = List(Unicode(), 

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

158 ).tag(config=True) 

159 code_to_run = Unicode('', 

160 help="Execute the given command string." 

161 ).tag(config=True) 

162 module_to_run = Unicode('', 

163 help="Run the module as a script." 

164 ).tag(config=True) 

165 gui = CaselessStrEnum(gui_keys, allow_none=True, 

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

167 ).tag(config=True) 

168 matplotlib = CaselessStrEnum(backend_keys, allow_none=True, 

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

170 the default matplotlib backend.""" 

171 ).tag(config=True) 

172 pylab = CaselessStrEnum(backend_keys, allow_none=True, 

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

174 selecting a particular matplotlib backend and loop integration. 

175 """ 

176 ).tag(config=True) 

177 pylab_import_all = Bool(True, 

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

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

180 

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

182 """ 

183 ).tag(config=True) 

184 ignore_cwd = Bool( 

185 False, 

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

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

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

189 ).tag(config=True) 

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

191 allow_none=True) 

192 # whether interact-loop should start 

193 interact = Bool(True) 

194 

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

196 @observe('user_ns') 

197 def _user_ns_changed(self, change): 

198 if self.shell is not None: 

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

200 self.shell.init_user_ns() 

201 

202 def init_path(self): 

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

204 

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

206 or `dist-packages` directory, 

207 so that it is after the standard library. 

208 

209 .. versionchanged:: 7.2 

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

211 .. versionchanged:: 8.0 

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

213 """ 

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

215 return 

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

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

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

219 break 

220 else: 

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

222 # back to original behavior of inserting at the front 

223 idx = 0 

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

225 

226 def init_shell(self): 

227 raise NotImplementedError("Override in subclasses") 

228 

229 def init_gui_pylab(self): 

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

231 enable = False 

232 shell = self.shell 

233 if self.pylab: 

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

235 key = self.pylab 

236 elif self.matplotlib: 

237 enable = shell.enable_matplotlib 

238 key = self.matplotlib 

239 elif self.gui: 

240 enable = shell.enable_gui 

241 key = self.gui 

242 

243 if not enable: 

244 return 

245 

246 try: 

247 r = enable(key) 

248 except ImportError: 

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

250 self.shell.showtraceback() 

251 return 

252 except Exception: 

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

254 self.shell.showtraceback() 

255 return 

256 

257 if isinstance(r, tuple): 

258 gui, backend = r[:2] 

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

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

261 if key == "auto": 

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

263 else: 

264 gui = r 

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

266 "eventloop=%s", gui) 

267 

268 def init_extensions(self): 

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

270 

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

272 the extensions listed in ``self.extensions``. 

273 """ 

274 try: 

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

276 extensions = ( 

277 self.default_extensions + self.extensions + self.extra_extensions 

278 ) 

279 for ext in extensions: 

280 try: 

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

282 self.shell.extension_manager.load_extension(ext) 

283 except: 

284 if self.reraise_ipython_extension_failures: 

285 raise 

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

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

288 ext=ext, 

289 location=self.profile_dir.location 

290 )) 

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

292 except: 

293 if self.reraise_ipython_extension_failures: 

294 raise 

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

296 

297 def init_code(self): 

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

299 self._run_startup_files() 

300 self._run_exec_lines() 

301 self._run_exec_files() 

302 

303 # Hide variables defined here from %who etc. 

304 if self.hide_initial_ns: 

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

306 

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

308 # should *not* be excluded from %whos 

309 self._run_cmd_line_code() 

310 self._run_module() 

311 

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

313 sys.stdout.flush() 

314 sys.stderr.flush() 

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

316 

317 def _run_exec_lines(self): 

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

319 if not self.exec_lines: 

320 return 

321 try: 

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

323 for line in self.exec_lines: 

324 try: 

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

326 line) 

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

328 except: 

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

330 "namespace: %s" % line) 

331 self.shell.showtraceback() 

332 except: 

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

334 self.shell.showtraceback() 

335 

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

337 try: 

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

339 except IOError: 

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

341 return 

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

343 # were run from a system shell. 

344 save_argv = sys.argv 

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

346 try: 

347 if os.path.isfile(full_filename): 

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

349 full_filename) 

350 # Ensure that __file__ is always defined to match Python 

351 # behavior. 

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

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

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

355 self.shell.safe_execfile_ipy(full_filename, 

356 shell_futures=shell_futures) 

357 else: 

358 # default to python, even without extension 

359 self.shell.safe_execfile(full_filename, 

360 self.shell.user_ns, 

361 shell_futures=shell_futures, 

362 raise_exceptions=True) 

363 finally: 

364 sys.argv = save_argv 

365 

366 def _run_startup_files(self): 

367 """Run files from profile startup directory""" 

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

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

370 ] 

371 startup_files = [] 

372 

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

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

375 python_startup = os.environ['PYTHONSTARTUP'] 

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

377 try: 

378 self._exec_file(python_startup) 

379 except: 

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

381 self.shell.showtraceback() 

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

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

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

385 if not startup_files: 

386 return 

387 

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

389 try: 

390 for fname in sorted(startup_files): 

391 self._exec_file(fname) 

392 except: 

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

394 self.shell.showtraceback() 

395 

396 def _run_exec_files(self): 

397 """Run files from IPythonApp.exec_files""" 

398 if not self.exec_files: 

399 return 

400 

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

402 try: 

403 for fname in self.exec_files: 

404 self._exec_file(fname) 

405 except: 

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

407 self.shell.showtraceback() 

408 

409 def _run_cmd_line_code(self): 

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

411 if self.code_to_run: 

412 line = self.code_to_run 

413 try: 

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

415 line) 

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

417 except: 

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

419 line) 

420 self.shell.showtraceback() 

421 if not self.interact: 

422 self.exit(1) 

423 

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

425 elif self.file_to_run: 

426 fname = self.file_to_run 

427 if os.path.isdir(fname): 

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

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

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

431 if not self.interact: 

432 self.exit(2) 

433 try: 

434 self._exec_file(fname, shell_futures=True) 

435 except: 

436 self.shell.showtraceback(tb_offset=4) 

437 if not self.interact: 

438 self.exit(1) 

439 

440 def _run_module(self): 

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

442 if self.module_to_run: 

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

444 # run using `python -m`. 

445 save_argv = sys.argv 

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

447 try: 

448 self.shell.safe_run_module(self.module_to_run, 

449 self.shell.user_ns) 

450 finally: 

451 sys.argv = save_argv