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
« 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"""
7# Copyright (c) IPython Development Team.
8# Distributed under the terms of the Modified BSD License.
10import glob
11from itertools import chain
12import os
13import sys
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
28#-----------------------------------------------------------------------------
29# Aliases and Flags
30#-----------------------------------------------------------------------------
32gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))
34backend_keys = sorted(pylabtools.backends.keys())
35backend_keys.insert(0, 'auto')
37shell_flags = {}
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 = ''
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)
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'
100#-----------------------------------------------------------------------------
101# Main classes and functions
102#-----------------------------------------------------------------------------
104class InteractiveShellApp(Configurable):
105 """A Mixin for applications that start InteractiveShell instances.
107 Provides configurables for loading extensions and executing files
108 as part of configuring a Shell environment.
110 The following methods should be called by the :meth:`initialize` method
111 of the subclass:
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)
123 extra_extensions = List(
124 DottedObjectName(),
125 help="""
126 Dotted module name(s) of one or more IPython extensions to load.
128 For specifying extra extensions to load on the command-line.
130 .. versionadded:: 7.10
131 """,
132 ).tag(config=True)
134 reraise_ipython_extension_failures = Bool(False,
135 help="Reraise exceptions encountered loading IPython extensions?",
136 ).tag(config=True)
138 # Extensions that are always loaded (not configurable)
139 default_extensions = List(Unicode(), [u'storemagic']).tag(config=False)
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)
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)
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.
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)
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()
202 def init_path(self):
203 """Add current working directory, '', to sys.path
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.
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, '')
226 def init_shell(self):
227 raise NotImplementedError("Override in subclasses")
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
243 if not enable:
244 return
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
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)
268 def init_extensions(self):
269 """Load all IPython extensions in IPythonApp.extensions.
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)
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()
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)
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()
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())
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()
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
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 = []
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
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()
396 def _run_exec_files(self):
397 """Run files from IPythonApp.exec_files"""
398 if not self.exec_files:
399 return
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()
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)
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)
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