##// END OF EJS Templates
Backport PR #12135: add configuration controlling whether the CWD gets added to sys.path
Matthias Bussonnier -
Show More
@@ -1,428 +1,440 b''
1 1 # encoding: utf-8
2 2 """
3 3 A mixin for :class:`~IPython.core.application.Application` classes that
4 4 launch InteractiveShell instances, load extensions, etc.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 import glob
11 11 from itertools import chain
12 12 import os
13 13 import sys
14 14
15 15 from traitlets.config.application import boolean_flag
16 16 from traitlets.config.configurable import Configurable
17 17 from traitlets.config.loader import Config
18 18 from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS
19 19 from IPython.core import pylabtools
20 20 from IPython.utils.contexts import preserve_keys
21 21 from IPython.utils.path import filefind
22 22 from traitlets import (
23 23 Unicode, Instance, List, Bool, CaselessStrEnum, observe,
24 24 )
25 25 from IPython.terminal import pt_inputhooks
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Aliases and Flags
29 29 #-----------------------------------------------------------------------------
30 30
31 31 gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))
32 32
33 33 backend_keys = sorted(pylabtools.backends.keys())
34 34 backend_keys.insert(0, 'auto')
35 35
36 36 shell_flags = {}
37 37
38 38 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
39 39 addflag('autoindent', 'InteractiveShell.autoindent',
40 40 'Turn on autoindenting.', 'Turn off autoindenting.'
41 41 )
42 42 addflag('automagic', 'InteractiveShell.automagic',
43 43 """Turn on the auto calling of magic commands. Type %%magic at the
44 44 IPython prompt for more information.""",
45 45 'Turn off the auto calling of magic commands.'
46 46 )
47 47 addflag('pdb', 'InteractiveShell.pdb',
48 48 "Enable auto calling the pdb debugger after every exception.",
49 49 "Disable auto calling the pdb debugger after every exception."
50 50 )
51 51 addflag('pprint', 'PlainTextFormatter.pprint',
52 52 "Enable auto pretty printing of results.",
53 53 "Disable auto pretty printing of results."
54 54 )
55 55 addflag('color-info', 'InteractiveShell.color_info',
56 56 """IPython can display information about objects via a set of functions,
57 57 and optionally can use colors for this, syntax highlighting
58 58 source code and various other elements. This is on by default, but can cause
59 59 problems with some pagers. If you see such problems, you can disable the
60 60 colours.""",
61 61 "Disable using colors for info related things."
62 62 )
63 addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd',
64 "Exclude the current working directory from sys.path",
65 "Include the current working directory in sys.path",
66 )
63 67 nosep_config = Config()
64 68 nosep_config.InteractiveShell.separate_in = ''
65 69 nosep_config.InteractiveShell.separate_out = ''
66 70 nosep_config.InteractiveShell.separate_out2 = ''
67 71
68 72 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
69 73 shell_flags['pylab'] = (
70 74 {'InteractiveShellApp' : {'pylab' : 'auto'}},
71 75 """Pre-load matplotlib and numpy for interactive use with
72 76 the default matplotlib backend."""
73 77 )
74 78 shell_flags['matplotlib'] = (
75 79 {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
76 80 """Configure matplotlib for interactive use with
77 81 the default matplotlib backend."""
78 82 )
79 83
80 84 # it's possible we don't want short aliases for *all* of these:
81 85 shell_aliases = dict(
82 86 autocall='InteractiveShell.autocall',
83 87 colors='InteractiveShell.colors',
84 88 logfile='InteractiveShell.logfile',
85 89 logappend='InteractiveShell.logappend',
86 90 c='InteractiveShellApp.code_to_run',
87 91 m='InteractiveShellApp.module_to_run',
88 92 ext='InteractiveShellApp.extra_extension',
89 93 gui='InteractiveShellApp.gui',
90 94 pylab='InteractiveShellApp.pylab',
91 95 matplotlib='InteractiveShellApp.matplotlib',
92 96 )
93 97 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
94 98
95 99 #-----------------------------------------------------------------------------
96 100 # Main classes and functions
97 101 #-----------------------------------------------------------------------------
98 102
99 103 class InteractiveShellApp(Configurable):
100 104 """A Mixin for applications that start InteractiveShell instances.
101 105
102 106 Provides configurables for loading extensions and executing files
103 107 as part of configuring a Shell environment.
104 108
105 109 The following methods should be called by the :meth:`initialize` method
106 110 of the subclass:
107 111
108 112 - :meth:`init_path`
109 113 - :meth:`init_shell` (to be implemented by the subclass)
110 114 - :meth:`init_gui_pylab`
111 115 - :meth:`init_extensions`
112 116 - :meth:`init_code`
113 117 """
114 118 extensions = List(Unicode(),
115 119 help="A list of dotted module names of IPython extensions to load."
116 120 ).tag(config=True)
117 121 extra_extension = Unicode('',
118 122 help="dotted module name of an IPython extension to load."
119 123 ).tag(config=True)
120 124
121 125 reraise_ipython_extension_failures = Bool(False,
122 126 help="Reraise exceptions encountered loading IPython extensions?",
123 127 ).tag(config=True)
124 128
125 129 # Extensions that are always loaded (not configurable)
126 130 default_extensions = List(Unicode(), [u'storemagic']).tag(config=False)
127 131
128 132 hide_initial_ns = Bool(True,
129 133 help="""Should variables loaded at startup (by startup files, exec_lines, etc.)
130 134 be hidden from tools like %who?"""
131 135 ).tag(config=True)
132 136
133 137 exec_files = List(Unicode(),
134 138 help="""List of files to run at IPython startup."""
135 139 ).tag(config=True)
136 140 exec_PYTHONSTARTUP = Bool(True,
137 141 help="""Run the file referenced by the PYTHONSTARTUP environment
138 142 variable at IPython startup."""
139 143 ).tag(config=True)
140 144 file_to_run = Unicode('',
141 145 help="""A file to be run""").tag(config=True)
142 146
143 147 exec_lines = List(Unicode(),
144 148 help="""lines of code to run at IPython startup."""
145 149 ).tag(config=True)
146 150 code_to_run = Unicode('',
147 151 help="Execute the given command string."
148 152 ).tag(config=True)
149 153 module_to_run = Unicode('',
150 154 help="Run the module as a script."
151 155 ).tag(config=True)
152 156 gui = CaselessStrEnum(gui_keys, allow_none=True,
153 157 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
154 158 ).tag(config=True)
155 159 matplotlib = CaselessStrEnum(backend_keys, allow_none=True,
156 160 help="""Configure matplotlib for interactive use with
157 161 the default matplotlib backend."""
158 162 ).tag(config=True)
159 163 pylab = CaselessStrEnum(backend_keys, allow_none=True,
160 164 help="""Pre-load matplotlib and numpy for interactive use,
161 165 selecting a particular matplotlib backend and loop integration.
162 166 """
163 167 ).tag(config=True)
164 168 pylab_import_all = Bool(True,
165 169 help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
166 170 and an ``import *`` is done from numpy and pylab, when using pylab mode.
167 171
168 172 When False, pylab mode should not import any names into the user namespace.
169 173 """
170 174 ).tag(config=True)
175 ignore_cwd = Bool(
176 False,
177 help="""If True, IPython will not add the current working directory to sys.path.
178 When False, the current working directory is added to sys.path, allowing imports
179 of modules defined in the current directory."""
180 ).tag(config=True)
171 181 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
172 182 allow_none=True)
173 183 # whether interact-loop should start
174 184 interact = Bool(True)
175 185
176 186 user_ns = Instance(dict, args=None, allow_none=True)
177 187 @observe('user_ns')
178 188 def _user_ns_changed(self, change):
179 189 if self.shell is not None:
180 190 self.shell.user_ns = change['new']
181 191 self.shell.init_user_ns()
182 192
183 193 def init_path(self):
184 194 """Add current working directory, '', to sys.path
185 195
186 196 Unlike Python's default, we insert before the first `site-packages`
187 197 or `dist-packages` directory,
188 198 so that it is after the standard library.
189 199
190 200 .. versionchanged:: 7.2
191 201 Try to insert after the standard library, instead of first.
202 .. versionchanged:: 8.0
203 Allow optionally not including the current directory in sys.path
192 204 """
193 if '' in sys.path:
205 if '' in sys.path or self.ignore_cwd:
194 206 return
195 207 for idx, path in enumerate(sys.path):
196 208 parent, last_part = os.path.split(path)
197 209 if last_part in {'site-packages', 'dist-packages'}:
198 210 break
199 211 else:
200 212 # no site-packages or dist-packages found (?!)
201 213 # back to original behavior of inserting at the front
202 214 idx = 0
203 215 sys.path.insert(idx, '')
204 216
205 217 def init_shell(self):
206 218 raise NotImplementedError("Override in subclasses")
207 219
208 220 def init_gui_pylab(self):
209 221 """Enable GUI event loop integration, taking pylab into account."""
210 222 enable = False
211 223 shell = self.shell
212 224 if self.pylab:
213 225 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
214 226 key = self.pylab
215 227 elif self.matplotlib:
216 228 enable = shell.enable_matplotlib
217 229 key = self.matplotlib
218 230 elif self.gui:
219 231 enable = shell.enable_gui
220 232 key = self.gui
221 233
222 234 if not enable:
223 235 return
224 236
225 237 try:
226 238 r = enable(key)
227 239 except ImportError:
228 240 self.log.warning("Eventloop or matplotlib integration failed. Is matplotlib installed?")
229 241 self.shell.showtraceback()
230 242 return
231 243 except Exception:
232 244 self.log.warning("GUI event loop or pylab initialization failed")
233 245 self.shell.showtraceback()
234 246 return
235 247
236 248 if isinstance(r, tuple):
237 249 gui, backend = r[:2]
238 250 self.log.info("Enabling GUI event loop integration, "
239 251 "eventloop=%s, matplotlib=%s", gui, backend)
240 252 if key == "auto":
241 253 print("Using matplotlib backend: %s" % backend)
242 254 else:
243 255 gui = r
244 256 self.log.info("Enabling GUI event loop integration, "
245 257 "eventloop=%s", gui)
246 258
247 259 def init_extensions(self):
248 260 """Load all IPython extensions in IPythonApp.extensions.
249 261
250 262 This uses the :meth:`ExtensionManager.load_extensions` to load all
251 263 the extensions listed in ``self.extensions``.
252 264 """
253 265 try:
254 266 self.log.debug("Loading IPython extensions...")
255 267 extensions = self.default_extensions + self.extensions
256 268 if self.extra_extension:
257 269 extensions.append(self.extra_extension)
258 270 for ext in extensions:
259 271 try:
260 272 self.log.info("Loading IPython extension: %s" % ext)
261 273 self.shell.extension_manager.load_extension(ext)
262 274 except:
263 275 if self.reraise_ipython_extension_failures:
264 276 raise
265 277 msg = ("Error in loading extension: {ext}\n"
266 278 "Check your config files in {location}".format(
267 279 ext=ext,
268 280 location=self.profile_dir.location
269 281 ))
270 282 self.log.warning(msg, exc_info=True)
271 283 except:
272 284 if self.reraise_ipython_extension_failures:
273 285 raise
274 286 self.log.warning("Unknown error in loading extensions:", exc_info=True)
275 287
276 288 def init_code(self):
277 289 """run the pre-flight code, specified via exec_lines"""
278 290 self._run_startup_files()
279 291 self._run_exec_lines()
280 292 self._run_exec_files()
281 293
282 294 # Hide variables defined here from %who etc.
283 295 if self.hide_initial_ns:
284 296 self.shell.user_ns_hidden.update(self.shell.user_ns)
285 297
286 298 # command-line execution (ipython -i script.py, ipython -m module)
287 299 # should *not* be excluded from %whos
288 300 self._run_cmd_line_code()
289 301 self._run_module()
290 302
291 303 # flush output, so itwon't be attached to the first cell
292 304 sys.stdout.flush()
293 305 sys.stderr.flush()
294 306
295 307 def _run_exec_lines(self):
296 308 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
297 309 if not self.exec_lines:
298 310 return
299 311 try:
300 312 self.log.debug("Running code from IPythonApp.exec_lines...")
301 313 for line in self.exec_lines:
302 314 try:
303 315 self.log.info("Running code in user namespace: %s" %
304 316 line)
305 317 self.shell.run_cell(line, store_history=False)
306 318 except:
307 319 self.log.warning("Error in executing line in user "
308 320 "namespace: %s" % line)
309 321 self.shell.showtraceback()
310 322 except:
311 323 self.log.warning("Unknown error in handling IPythonApp.exec_lines:")
312 324 self.shell.showtraceback()
313 325
314 326 def _exec_file(self, fname, shell_futures=False):
315 327 try:
316 328 full_filename = filefind(fname, [u'.', self.ipython_dir])
317 329 except IOError:
318 330 self.log.warning("File not found: %r"%fname)
319 331 return
320 332 # Make sure that the running script gets a proper sys.argv as if it
321 333 # were run from a system shell.
322 334 save_argv = sys.argv
323 335 sys.argv = [full_filename] + self.extra_args[1:]
324 336 try:
325 337 if os.path.isfile(full_filename):
326 338 self.log.info("Running file in user namespace: %s" %
327 339 full_filename)
328 340 # Ensure that __file__ is always defined to match Python
329 341 # behavior.
330 342 with preserve_keys(self.shell.user_ns, '__file__'):
331 343 self.shell.user_ns['__file__'] = fname
332 344 if full_filename.endswith('.ipy') or full_filename.endswith('.ipynb'):
333 345 self.shell.safe_execfile_ipy(full_filename,
334 346 shell_futures=shell_futures)
335 347 else:
336 348 # default to python, even without extension
337 349 self.shell.safe_execfile(full_filename,
338 350 self.shell.user_ns,
339 351 shell_futures=shell_futures,
340 352 raise_exceptions=True)
341 353 finally:
342 354 sys.argv = save_argv
343 355
344 356 def _run_startup_files(self):
345 357 """Run files from profile startup directory"""
346 358 startup_dirs = [self.profile_dir.startup_dir] + [
347 359 os.path.join(p, 'startup') for p in chain(ENV_CONFIG_DIRS, SYSTEM_CONFIG_DIRS)
348 360 ]
349 361 startup_files = []
350 362
351 363 if self.exec_PYTHONSTARTUP and os.environ.get('PYTHONSTARTUP', False) and \
352 364 not (self.file_to_run or self.code_to_run or self.module_to_run):
353 365 python_startup = os.environ['PYTHONSTARTUP']
354 366 self.log.debug("Running PYTHONSTARTUP file %s...", python_startup)
355 367 try:
356 368 self._exec_file(python_startup)
357 369 except:
358 370 self.log.warning("Unknown error in handling PYTHONSTARTUP file %s:", python_startup)
359 371 self.shell.showtraceback()
360 372 for startup_dir in startup_dirs[::-1]:
361 373 startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
362 374 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
363 375 if not startup_files:
364 376 return
365 377
366 378 self.log.debug("Running startup files from %s...", startup_dir)
367 379 try:
368 380 for fname in sorted(startup_files):
369 381 self._exec_file(fname)
370 382 except:
371 383 self.log.warning("Unknown error in handling startup files:")
372 384 self.shell.showtraceback()
373 385
374 386 def _run_exec_files(self):
375 387 """Run files from IPythonApp.exec_files"""
376 388 if not self.exec_files:
377 389 return
378 390
379 391 self.log.debug("Running files in IPythonApp.exec_files...")
380 392 try:
381 393 for fname in self.exec_files:
382 394 self._exec_file(fname)
383 395 except:
384 396 self.log.warning("Unknown error in handling IPythonApp.exec_files:")
385 397 self.shell.showtraceback()
386 398
387 399 def _run_cmd_line_code(self):
388 400 """Run code or file specified at the command-line"""
389 401 if self.code_to_run:
390 402 line = self.code_to_run
391 403 try:
392 404 self.log.info("Running code given at command line (c=): %s" %
393 405 line)
394 406 self.shell.run_cell(line, store_history=False)
395 407 except:
396 408 self.log.warning("Error in executing line in user namespace: %s" %
397 409 line)
398 410 self.shell.showtraceback()
399 411 if not self.interact:
400 412 self.exit(1)
401 413
402 414 # Like Python itself, ignore the second if the first of these is present
403 415 elif self.file_to_run:
404 416 fname = self.file_to_run
405 417 if os.path.isdir(fname):
406 418 fname = os.path.join(fname, "__main__.py")
407 419 if not os.path.exists(fname):
408 420 self.log.warning("File '%s' doesn't exist", fname)
409 421 self.exit(2)
410 422 try:
411 423 self._exec_file(fname, shell_futures=True)
412 424 except:
413 425 self.shell.showtraceback(tb_offset=4)
414 426 if not self.interact:
415 427 self.exit(1)
416 428
417 429 def _run_module(self):
418 430 """Run module specified at the command-line."""
419 431 if self.module_to_run:
420 432 # Make sure that the module gets a proper sys.argv as if it were
421 433 # run using `python -m`.
422 434 save_argv = sys.argv
423 435 sys.argv = [sys.executable] + self.extra_args
424 436 try:
425 437 self.shell.safe_run_module(self.module_to_run,
426 438 self.shell.user_ns)
427 439 finally:
428 440 sys.argv = save_argv
General Comments 0
You need to be logged in to leave comments. Login now