##// END OF EJS Templates
Fix #14282, and please linter.
Matthias Bussonnier -
Show More
@@ -1,488 +1,492 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for IPython.
3 An application for IPython.
4
4
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating configurables.
6 handling configuration and creating configurables.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 import atexit
15 import atexit
16 from copy import deepcopy
16 from copy import deepcopy
17 import logging
17 import logging
18 import os
18 import os
19 import shutil
19 import shutil
20 import sys
20 import sys
21
21
22 from pathlib import Path
22 from pathlib import Path
23
23
24 from traitlets.config.application import Application, catch_config_error
24 from traitlets.config.application import Application, catch_config_error
25 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
25 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
26 from IPython.core import release, crashhandler
26 from IPython.core import release, crashhandler
27 from IPython.core.profiledir import ProfileDir, ProfileDirError
27 from IPython.core.profiledir import ProfileDir, ProfileDirError
28 from IPython.paths import get_ipython_dir, get_ipython_package_dir
28 from IPython.paths import get_ipython_dir, get_ipython_package_dir
29 from IPython.utils.path import ensure_dir_exists
29 from IPython.utils.path import ensure_dir_exists
30 from traitlets import (
30 from traitlets import (
31 List, Unicode, Type, Bool, Set, Instance, Undefined,
31 List, Unicode, Type, Bool, Set, Instance, Undefined,
32 default, observe,
32 default, observe,
33 )
33 )
34
34
35 if os.name == "nt":
35 if os.name == "nt":
36 programdata = os.environ.get("PROGRAMDATA", None)
36 programdata = os.environ.get("PROGRAMDATA", None)
37 if programdata is not None:
37 if programdata is not None:
38 SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")]
38 SYSTEM_CONFIG_DIRS = [str(Path(programdata) / "ipython")]
39 else: # PROGRAMDATA is not defined by default on XP.
39 else: # PROGRAMDATA is not defined by default on XP.
40 SYSTEM_CONFIG_DIRS = []
40 SYSTEM_CONFIG_DIRS = []
41 else:
41 else:
42 SYSTEM_CONFIG_DIRS = [
42 SYSTEM_CONFIG_DIRS = [
43 "/usr/local/etc/ipython",
43 "/usr/local/etc/ipython",
44 "/etc/ipython",
44 "/etc/ipython",
45 ]
45 ]
46
46
47
47
48 ENV_CONFIG_DIRS = []
48 ENV_CONFIG_DIRS = []
49 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
49 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
50 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
50 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
51 # only add ENV_CONFIG if sys.prefix is not already included
51 # only add ENV_CONFIG if sys.prefix is not already included
52 ENV_CONFIG_DIRS.append(_env_config_dir)
52 ENV_CONFIG_DIRS.append(_env_config_dir)
53
53
54
54
55 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
55 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
56 if _envvar in {None, ''}:
56 if _envvar in {None, ''}:
57 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
57 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
58 else:
58 else:
59 if _envvar.lower() in {'1','true'}:
59 if _envvar.lower() in {'1','true'}:
60 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
60 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
61 elif _envvar.lower() in {'0','false'} :
61 elif _envvar.lower() in {'0','false'} :
62 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
62 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
63 else:
63 else:
64 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
64 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
65
65
66 # aliases and flags
66 # aliases and flags
67
67
68 base_aliases = {}
68 base_aliases = {}
69 if isinstance(Application.aliases, dict):
69 if isinstance(Application.aliases, dict):
70 # traitlets 5
70 # traitlets 5
71 base_aliases.update(Application.aliases)
71 base_aliases.update(Application.aliases)
72 base_aliases.update(
72 base_aliases.update(
73 {
73 {
74 "profile-dir": "ProfileDir.location",
74 "profile-dir": "ProfileDir.location",
75 "profile": "BaseIPythonApplication.profile",
75 "profile": "BaseIPythonApplication.profile",
76 "ipython-dir": "BaseIPythonApplication.ipython_dir",
76 "ipython-dir": "BaseIPythonApplication.ipython_dir",
77 "log-level": "Application.log_level",
77 "log-level": "Application.log_level",
78 "config": "BaseIPythonApplication.extra_config_file",
78 "config": "BaseIPythonApplication.extra_config_file",
79 }
79 }
80 )
80 )
81
81
82 base_flags = dict()
82 base_flags = dict()
83 if isinstance(Application.flags, dict):
83 if isinstance(Application.flags, dict):
84 # traitlets 5
84 # traitlets 5
85 base_flags.update(Application.flags)
85 base_flags.update(Application.flags)
86 base_flags.update(
86 base_flags.update(
87 dict(
87 dict(
88 debug=(
88 debug=(
89 {"Application": {"log_level": logging.DEBUG}},
89 {"Application": {"log_level": logging.DEBUG}},
90 "set log level to logging.DEBUG (maximize logging output)",
90 "set log level to logging.DEBUG (maximize logging output)",
91 ),
91 ),
92 quiet=(
92 quiet=(
93 {"Application": {"log_level": logging.CRITICAL}},
93 {"Application": {"log_level": logging.CRITICAL}},
94 "set log level to logging.CRITICAL (minimize logging output)",
94 "set log level to logging.CRITICAL (minimize logging output)",
95 ),
95 ),
96 init=(
96 init=(
97 {
97 {
98 "BaseIPythonApplication": {
98 "BaseIPythonApplication": {
99 "copy_config_files": True,
99 "copy_config_files": True,
100 "auto_create": True,
100 "auto_create": True,
101 }
101 }
102 },
102 },
103 """Initialize profile with default config files. This is equivalent
103 """Initialize profile with default config files. This is equivalent
104 to running `ipython profile create <profile>` prior to startup.
104 to running `ipython profile create <profile>` prior to startup.
105 """,
105 """,
106 ),
106 ),
107 )
107 )
108 )
108 )
109
109
110
110
111 class ProfileAwareConfigLoader(PyFileConfigLoader):
111 class ProfileAwareConfigLoader(PyFileConfigLoader):
112 """A Python file config loader that is aware of IPython profiles."""
112 """A Python file config loader that is aware of IPython profiles."""
113 def load_subconfig(self, fname, path=None, profile=None):
113 def load_subconfig(self, fname, path=None, profile=None):
114 if profile is not None:
114 if profile is not None:
115 try:
115 try:
116 profile_dir = ProfileDir.find_profile_dir_by_name(
116 profile_dir = ProfileDir.find_profile_dir_by_name(
117 get_ipython_dir(),
117 get_ipython_dir(),
118 profile,
118 profile,
119 )
119 )
120 except ProfileDirError:
120 except ProfileDirError:
121 return
121 return
122 path = profile_dir.location
122 path = profile_dir.location
123 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
123 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
124
124
125 class BaseIPythonApplication(Application):
125 class BaseIPythonApplication(Application):
126 name = "ipython"
126 name = "ipython"
127 description = "IPython: an enhanced interactive Python shell."
127 description = "IPython: an enhanced interactive Python shell."
128 version = Unicode(release.version)
128 version = Unicode(release.version)
129
129
130 aliases = base_aliases
130 aliases = base_aliases
131 flags = base_flags
131 flags = base_flags
132 classes = List([ProfileDir])
132 classes = List([ProfileDir])
133
133
134 # enable `load_subconfig('cfg.py', profile='name')`
134 # enable `load_subconfig('cfg.py', profile='name')`
135 python_config_loader_class = ProfileAwareConfigLoader
135 python_config_loader_class = ProfileAwareConfigLoader
136
136
137 # Track whether the config_file has changed,
137 # Track whether the config_file has changed,
138 # because some logic happens only if we aren't using the default.
138 # because some logic happens only if we aren't using the default.
139 config_file_specified = Set()
139 config_file_specified = Set()
140
140
141 config_file_name = Unicode()
141 config_file_name = Unicode()
142 @default('config_file_name')
142 @default('config_file_name')
143 def _config_file_name_default(self):
143 def _config_file_name_default(self):
144 return self.name.replace('-','_') + u'_config.py'
144 return self.name.replace('-','_') + u'_config.py'
145 @observe('config_file_name')
145 @observe('config_file_name')
146 def _config_file_name_changed(self, change):
146 def _config_file_name_changed(self, change):
147 if change['new'] != change['old']:
147 if change['new'] != change['old']:
148 self.config_file_specified.add(change['new'])
148 self.config_file_specified.add(change['new'])
149
149
150 # The directory that contains IPython's builtin profiles.
150 # The directory that contains IPython's builtin profiles.
151 builtin_profile_dir = Unicode(
151 builtin_profile_dir = Unicode(
152 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
152 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
153 )
153 )
154
154
155 config_file_paths = List(Unicode())
155 config_file_paths = List(Unicode())
156 @default('config_file_paths')
156 @default('config_file_paths')
157 def _config_file_paths_default(self):
157 def _config_file_paths_default(self):
158 return []
158 return []
159
159
160 extra_config_file = Unicode(
160 extra_config_file = Unicode(
161 help="""Path to an extra config file to load.
161 help="""Path to an extra config file to load.
162
162
163 If specified, load this config file in addition to any other IPython config.
163 If specified, load this config file in addition to any other IPython config.
164 """).tag(config=True)
164 """).tag(config=True)
165 @observe('extra_config_file')
165 @observe('extra_config_file')
166 def _extra_config_file_changed(self, change):
166 def _extra_config_file_changed(self, change):
167 old = change['old']
167 old = change['old']
168 new = change['new']
168 new = change['new']
169 try:
169 try:
170 self.config_files.remove(old)
170 self.config_files.remove(old)
171 except ValueError:
171 except ValueError:
172 pass
172 pass
173 self.config_file_specified.add(new)
173 self.config_file_specified.add(new)
174 self.config_files.append(new)
174 self.config_files.append(new)
175
175
176 profile = Unicode(u'default',
176 profile = Unicode(u'default',
177 help="""The IPython profile to use."""
177 help="""The IPython profile to use."""
178 ).tag(config=True)
178 ).tag(config=True)
179
179
180 @observe('profile')
180 @observe('profile')
181 def _profile_changed(self, change):
181 def _profile_changed(self, change):
182 self.builtin_profile_dir = os.path.join(
182 self.builtin_profile_dir = os.path.join(
183 get_ipython_package_dir(), u'config', u'profile', change['new']
183 get_ipython_package_dir(), u'config', u'profile', change['new']
184 )
184 )
185
185
186 add_ipython_dir_to_sys_path = Bool(
186 add_ipython_dir_to_sys_path = Bool(
187 False,
187 False,
188 """Should the IPython profile directory be added to sys path ?
188 """Should the IPython profile directory be added to sys path ?
189
189
190 This option was non-existing before IPython 8.0, and ipython_dir was added to
190 This option was non-existing before IPython 8.0, and ipython_dir was added to
191 sys path to allow import of extensions present there. This was historical
191 sys path to allow import of extensions present there. This was historical
192 baggage from when pip did not exist. This now default to false,
192 baggage from when pip did not exist. This now default to false,
193 but can be set to true for legacy reasons.
193 but can be set to true for legacy reasons.
194 """,
194 """,
195 ).tag(config=True)
195 ).tag(config=True)
196
196
197 ipython_dir = Unicode(
197 ipython_dir = Unicode(
198 help="""
198 help="""
199 The name of the IPython directory. This directory is used for logging
199 The name of the IPython directory. This directory is used for logging
200 configuration (through profiles), history storage, etc. The default
200 configuration (through profiles), history storage, etc. The default
201 is usually $HOME/.ipython. This option can also be specified through
201 is usually $HOME/.ipython. This option can also be specified through
202 the environment variable IPYTHONDIR.
202 the environment variable IPYTHONDIR.
203 """
203 """
204 ).tag(config=True)
204 ).tag(config=True)
205 @default('ipython_dir')
205 @default('ipython_dir')
206 def _ipython_dir_default(self):
206 def _ipython_dir_default(self):
207 d = get_ipython_dir()
207 d = get_ipython_dir()
208 self._ipython_dir_changed({
208 self._ipython_dir_changed({
209 'name': 'ipython_dir',
209 'name': 'ipython_dir',
210 'old': d,
210 'old': d,
211 'new': d,
211 'new': d,
212 })
212 })
213 return d
213 return d
214
214
215 _in_init_profile_dir = False
215 _in_init_profile_dir = False
216
216 profile_dir = Instance(ProfileDir, allow_none=True)
217 profile_dir = Instance(ProfileDir, allow_none=True)
218
217 @default('profile_dir')
219 @default('profile_dir')
218 def _profile_dir_default(self):
220 def _profile_dir_default(self):
219 # avoid recursion
221 # avoid recursion
220 if self._in_init_profile_dir:
222 if self._in_init_profile_dir:
221 return
223 return
222 # profile_dir requested early, force initialization
224 # profile_dir requested early, force initialization
223 self.init_profile_dir()
225 self.init_profile_dir()
224 return self.profile_dir
226 return self.profile_dir
225
227
226 overwrite = Bool(False,
228 overwrite = Bool(False,
227 help="""Whether to overwrite existing config files when copying"""
229 help="""Whether to overwrite existing config files when copying"""
228 ).tag(config=True)
230 ).tag(config=True)
231
229 auto_create = Bool(False,
232 auto_create = Bool(False,
230 help="""Whether to create profile dir if it doesn't exist"""
233 help="""Whether to create profile dir if it doesn't exist"""
231 ).tag(config=True)
234 ).tag(config=True)
232
235
233 config_files = List(Unicode())
236 config_files = List(Unicode())
237
234 @default('config_files')
238 @default('config_files')
235 def _config_files_default(self):
239 def _config_files_default(self):
236 return [self.config_file_name]
240 return [self.config_file_name]
237
241
238 copy_config_files = Bool(False,
242 copy_config_files = Bool(False,
239 help="""Whether to install the default config files into the profile dir.
243 help="""Whether to install the default config files into the profile dir.
240 If a new profile is being created, and IPython contains config files for that
244 If a new profile is being created, and IPython contains config files for that
241 profile, then they will be staged into the new directory. Otherwise,
245 profile, then they will be staged into the new directory. Otherwise,
242 default config files will be automatically generated.
246 default config files will be automatically generated.
243 """).tag(config=True)
247 """).tag(config=True)
244
248
245 verbose_crash = Bool(False,
249 verbose_crash = Bool(False,
246 help="""Create a massive crash report when IPython encounters what may be an
250 help="""Create a massive crash report when IPython encounters what may be an
247 internal error. The default is to append a short message to the
251 internal error. The default is to append a short message to the
248 usual traceback""").tag(config=True)
252 usual traceback""").tag(config=True)
249
253
250 # The class to use as the crash handler.
254 # The class to use as the crash handler.
251 crash_handler_class = Type(crashhandler.CrashHandler)
255 crash_handler_class = Type(crashhandler.CrashHandler)
252
256
253 @catch_config_error
257 @catch_config_error
254 def __init__(self, **kwargs):
258 def __init__(self, **kwargs):
255 super(BaseIPythonApplication, self).__init__(**kwargs)
259 super(BaseIPythonApplication, self).__init__(**kwargs)
256 # ensure current working directory exists
260 # ensure current working directory exists
257 try:
261 try:
258 os.getcwd()
262 os.getcwd()
259 except:
263 except:
260 # exit if cwd doesn't exist
264 # exit if cwd doesn't exist
261 self.log.error("Current working directory doesn't exist.")
265 self.log.error("Current working directory doesn't exist.")
262 self.exit(1)
266 self.exit(1)
263
267
264 #-------------------------------------------------------------------------
268 #-------------------------------------------------------------------------
265 # Various stages of Application creation
269 # Various stages of Application creation
266 #-------------------------------------------------------------------------
270 #-------------------------------------------------------------------------
267
271
268 def init_crash_handler(self):
272 def init_crash_handler(self):
269 """Create a crash handler, typically setting sys.excepthook to it."""
273 """Create a crash handler, typically setting sys.excepthook to it."""
270 self.crash_handler = self.crash_handler_class(self)
274 self.crash_handler = self.crash_handler_class(self)
271 sys.excepthook = self.excepthook
275 sys.excepthook = self.excepthook
272 def unset_crashhandler():
276 def unset_crashhandler():
273 sys.excepthook = sys.__excepthook__
277 sys.excepthook = sys.__excepthook__
274 atexit.register(unset_crashhandler)
278 atexit.register(unset_crashhandler)
275
279
276 def excepthook(self, etype, evalue, tb):
280 def excepthook(self, etype, evalue, tb):
277 """this is sys.excepthook after init_crashhandler
281 """this is sys.excepthook after init_crashhandler
278
282
279 set self.verbose_crash=True to use our full crashhandler, instead of
283 set self.verbose_crash=True to use our full crashhandler, instead of
280 a regular traceback with a short message (crash_handler_lite)
284 a regular traceback with a short message (crash_handler_lite)
281 """
285 """
282
286
283 if self.verbose_crash:
287 if self.verbose_crash:
284 return self.crash_handler(etype, evalue, tb)
288 return self.crash_handler(etype, evalue, tb)
285 else:
289 else:
286 return crashhandler.crash_handler_lite(etype, evalue, tb)
290 return crashhandler.crash_handler_lite(etype, evalue, tb)
287
291
288 @observe('ipython_dir')
292 @observe('ipython_dir')
289 def _ipython_dir_changed(self, change):
293 def _ipython_dir_changed(self, change):
290 old = change['old']
294 old = change['old']
291 new = change['new']
295 new = change['new']
292 if old is not Undefined:
296 if old is not Undefined:
293 str_old = os.path.abspath(old)
297 str_old = os.path.abspath(old)
294 if str_old in sys.path:
298 if str_old in sys.path:
295 sys.path.remove(str_old)
299 sys.path.remove(str_old)
296 if self.add_ipython_dir_to_sys_path:
300 if self.add_ipython_dir_to_sys_path:
297 str_path = os.path.abspath(new)
301 str_path = os.path.abspath(new)
298 sys.path.append(str_path)
302 sys.path.append(str_path)
299 ensure_dir_exists(new)
303 ensure_dir_exists(new)
300 readme = os.path.join(new, "README")
304 readme = os.path.join(new, "README")
301 readme_src = os.path.join(
305 readme_src = os.path.join(
302 get_ipython_package_dir(), "config", "profile", "README"
306 get_ipython_package_dir(), "config", "profile", "README"
303 )
307 )
304 if not os.path.exists(readme) and os.path.exists(readme_src):
308 if not os.path.exists(readme) and os.path.exists(readme_src):
305 shutil.copy(readme_src, readme)
309 shutil.copy(readme_src, readme)
306 for d in ("extensions", "nbextensions"):
310 for d in ("extensions", "nbextensions"):
307 path = os.path.join(new, d)
311 path = os.path.join(new, d)
308 try:
312 try:
309 ensure_dir_exists(path)
313 ensure_dir_exists(path)
310 except OSError as e:
314 except OSError as e:
311 # this will not be EEXIST
315 # this will not be EEXIST
312 self.log.error("couldn't create path %s: %s", path, e)
316 self.log.error("couldn't create path %s: %s", path, e)
313 self.log.debug("IPYTHONDIR set to: %s", new)
317 self.log.debug("IPYTHONDIR set to: %s", new)
314
318
315 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
319 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
316 """Load the config file.
320 """Load the config file.
317
321
318 By default, errors in loading config are handled, and a warning
322 By default, errors in loading config are handled, and a warning
319 printed on screen. For testing, the suppress_errors option is set
323 printed on screen. For testing, the suppress_errors option is set
320 to False, so errors will make tests fail.
324 to False, so errors will make tests fail.
321
325
322 `suppress_errors` default value is to be `None` in which case the
326 `suppress_errors` default value is to be `None` in which case the
323 behavior default to the one of `traitlets.Application`.
327 behavior default to the one of `traitlets.Application`.
324
328
325 The default value can be set :
329 The default value can be set :
326 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
330 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
327 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
331 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
328 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
332 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
329
333
330 Any other value are invalid, and will make IPython exit with a non-zero return code.
334 Any other value are invalid, and will make IPython exit with a non-zero return code.
331 """
335 """
332
336
333
337
334 self.log.debug("Searching path %s for config files", self.config_file_paths)
338 self.log.debug("Searching path %s for config files", self.config_file_paths)
335 base_config = 'ipython_config.py'
339 base_config = 'ipython_config.py'
336 self.log.debug("Attempting to load config file: %s" %
340 self.log.debug("Attempting to load config file: %s" %
337 base_config)
341 base_config)
338 try:
342 try:
339 if suppress_errors is not None:
343 if suppress_errors is not None:
340 old_value = Application.raise_config_file_errors
344 old_value = Application.raise_config_file_errors
341 Application.raise_config_file_errors = not suppress_errors;
345 Application.raise_config_file_errors = not suppress_errors;
342 Application.load_config_file(
346 Application.load_config_file(
343 self,
347 self,
344 base_config,
348 base_config,
345 path=self.config_file_paths
349 path=self.config_file_paths
346 )
350 )
347 except ConfigFileNotFound:
351 except ConfigFileNotFound:
348 # ignore errors loading parent
352 # ignore errors loading parent
349 self.log.debug("Config file %s not found", base_config)
353 self.log.debug("Config file %s not found", base_config)
350 pass
354 pass
351 if suppress_errors is not None:
355 if suppress_errors is not None:
352 Application.raise_config_file_errors = old_value
356 Application.raise_config_file_errors = old_value
353
357
354 for config_file_name in self.config_files:
358 for config_file_name in self.config_files:
355 if not config_file_name or config_file_name == base_config:
359 if not config_file_name or config_file_name == base_config:
356 continue
360 continue
357 self.log.debug("Attempting to load config file: %s" %
361 self.log.debug("Attempting to load config file: %s" %
358 self.config_file_name)
362 self.config_file_name)
359 try:
363 try:
360 Application.load_config_file(
364 Application.load_config_file(
361 self,
365 self,
362 config_file_name,
366 config_file_name,
363 path=self.config_file_paths
367 path=self.config_file_paths
364 )
368 )
365 except ConfigFileNotFound:
369 except ConfigFileNotFound:
366 # Only warn if the default config file was NOT being used.
370 # Only warn if the default config file was NOT being used.
367 if config_file_name in self.config_file_specified:
371 if config_file_name in self.config_file_specified:
368 msg = self.log.warning
372 msg = self.log.warning
369 else:
373 else:
370 msg = self.log.debug
374 msg = self.log.debug
371 msg("Config file not found, skipping: %s", config_file_name)
375 msg("Config file not found, skipping: %s", config_file_name)
372 except Exception:
376 except Exception:
373 # For testing purposes.
377 # For testing purposes.
374 if not suppress_errors:
378 if not suppress_errors:
375 raise
379 raise
376 self.log.warning("Error loading config file: %s" %
380 self.log.warning("Error loading config file: %s" %
377 self.config_file_name, exc_info=True)
381 self.config_file_name, exc_info=True)
378
382
379 def init_profile_dir(self):
383 def init_profile_dir(self):
380 """initialize the profile dir"""
384 """initialize the profile dir"""
381 self._in_init_profile_dir = True
385 self._in_init_profile_dir = True
382 if self.profile_dir is not None:
386 if self.profile_dir is not None:
383 # already ran
387 # already ran
384 return
388 return
385 if 'ProfileDir.location' not in self.config:
389 if 'ProfileDir.location' not in self.config:
386 # location not specified, find by profile name
390 # location not specified, find by profile name
387 try:
391 try:
388 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
392 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
389 except ProfileDirError:
393 except ProfileDirError:
390 # not found, maybe create it (always create default profile)
394 # not found, maybe create it (always create default profile)
391 if self.auto_create or self.profile == 'default':
395 if self.auto_create or self.profile == 'default':
392 try:
396 try:
393 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
397 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
394 except ProfileDirError:
398 except ProfileDirError:
395 self.log.fatal("Could not create profile: %r"%self.profile)
399 self.log.fatal("Could not create profile: %r"%self.profile)
396 self.exit(1)
400 self.exit(1)
397 else:
401 else:
398 self.log.info("Created profile dir: %r"%p.location)
402 self.log.info("Created profile dir: %r"%p.location)
399 else:
403 else:
400 self.log.fatal("Profile %r not found."%self.profile)
404 self.log.fatal("Profile %r not found."%self.profile)
401 self.exit(1)
405 self.exit(1)
402 else:
406 else:
403 self.log.debug("Using existing profile dir: %r", p.location)
407 self.log.debug("Using existing profile dir: %r", p.location)
404 else:
408 else:
405 location = self.config.ProfileDir.location
409 location = self.config.ProfileDir.location
406 # location is fully specified
410 # location is fully specified
407 try:
411 try:
408 p = ProfileDir.find_profile_dir(location, self.config)
412 p = ProfileDir.find_profile_dir(location, self.config)
409 except ProfileDirError:
413 except ProfileDirError:
410 # not found, maybe create it
414 # not found, maybe create it
411 if self.auto_create:
415 if self.auto_create:
412 try:
416 try:
413 p = ProfileDir.create_profile_dir(location, self.config)
417 p = ProfileDir.create_profile_dir(location, self.config)
414 except ProfileDirError:
418 except ProfileDirError:
415 self.log.fatal("Could not create profile directory: %r"%location)
419 self.log.fatal("Could not create profile directory: %r"%location)
416 self.exit(1)
420 self.exit(1)
417 else:
421 else:
418 self.log.debug("Creating new profile dir: %r"%location)
422 self.log.debug("Creating new profile dir: %r"%location)
419 else:
423 else:
420 self.log.fatal("Profile directory %r not found."%location)
424 self.log.fatal("Profile directory %r not found."%location)
421 self.exit(1)
425 self.exit(1)
422 else:
426 else:
423 self.log.debug("Using existing profile dir: %r", p.location)
427 self.log.debug("Using existing profile dir: %r", p.location)
424 # if profile_dir is specified explicitly, set profile name
428 # if profile_dir is specified explicitly, set profile name
425 dir_name = os.path.basename(p.location)
429 dir_name = os.path.basename(p.location)
426 if dir_name.startswith('profile_'):
430 if dir_name.startswith('profile_'):
427 self.profile = dir_name[8:]
431 self.profile = dir_name[8:]
428
432
429 self.profile_dir = p
433 self.profile_dir = p
430 self.config_file_paths.append(p.location)
434 self.config_file_paths.append(p.location)
431 self._in_init_profile_dir = False
435 self._in_init_profile_dir = False
432
436
433 def init_config_files(self):
437 def init_config_files(self):
434 """[optionally] copy default config files into profile dir."""
438 """[optionally] copy default config files into profile dir."""
435 self.config_file_paths.extend(ENV_CONFIG_DIRS)
439 self.config_file_paths.extend(ENV_CONFIG_DIRS)
436 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
440 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
437 # copy config files
441 # copy config files
438 path = Path(self.builtin_profile_dir)
442 path = Path(self.builtin_profile_dir)
439 if self.copy_config_files:
443 if self.copy_config_files:
440 src = self.profile
444 src = self.profile
441
445
442 cfg = self.config_file_name
446 cfg = self.config_file_name
443 if path and (path / cfg).exists():
447 if path and (path / cfg).exists():
444 self.log.warning(
448 self.log.warning(
445 "Staging %r from %s into %r [overwrite=%s]"
449 "Staging %r from %s into %r [overwrite=%s]"
446 % (cfg, src, self.profile_dir.location, self.overwrite)
450 % (cfg, src, self.profile_dir.location, self.overwrite)
447 )
451 )
448 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
452 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
449 else:
453 else:
450 self.stage_default_config_file()
454 self.stage_default_config_file()
451 else:
455 else:
452 # Still stage *bundled* config files, but not generated ones
456 # Still stage *bundled* config files, but not generated ones
453 # This is necessary for `ipython profile=sympy` to load the profile
457 # This is necessary for `ipython profile=sympy` to load the profile
454 # on the first go
458 # on the first go
455 files = path.glob("*.py")
459 files = path.glob("*.py")
456 for fullpath in files:
460 for fullpath in files:
457 cfg = fullpath.name
461 cfg = fullpath.name
458 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
462 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
459 # file was copied
463 # file was copied
460 self.log.warning("Staging bundled %s from %s into %r"%(
464 self.log.warning("Staging bundled %s from %s into %r"%(
461 cfg, self.profile, self.profile_dir.location)
465 cfg, self.profile, self.profile_dir.location)
462 )
466 )
463
467
464
468
465 def stage_default_config_file(self):
469 def stage_default_config_file(self):
466 """auto generate default config file, and stage it into the profile."""
470 """auto generate default config file, and stage it into the profile."""
467 s = self.generate_config_file()
471 s = self.generate_config_file()
468 config_file = Path(self.profile_dir.location) / self.config_file_name
472 config_file = Path(self.profile_dir.location) / self.config_file_name
469 if self.overwrite or not config_file.exists():
473 if self.overwrite or not config_file.exists():
470 self.log.warning("Generating default config file: %r", (config_file))
474 self.log.warning("Generating default config file: %r", (config_file))
471 config_file.write_text(s, encoding="utf-8")
475 config_file.write_text(s, encoding="utf-8")
472
476
473 @catch_config_error
477 @catch_config_error
474 def initialize(self, argv=None):
478 def initialize(self, argv=None):
475 # don't hook up crash handler before parsing command-line
479 # don't hook up crash handler before parsing command-line
476 self.parse_command_line(argv)
480 self.parse_command_line(argv)
477 self.init_crash_handler()
481 self.init_crash_handler()
478 if self.subapp is not None:
482 if self.subapp is not None:
479 # stop here if subapp is taking over
483 # stop here if subapp is taking over
480 return
484 return
481 # save a copy of CLI config to re-load after config files
485 # save a copy of CLI config to re-load after config files
482 # so that it has highest priority
486 # so that it has highest priority
483 cl_config = deepcopy(self.config)
487 cl_config = deepcopy(self.config)
484 self.init_profile_dir()
488 self.init_profile_dir()
485 self.init_config_files()
489 self.init_config_files()
486 self.load_config_file()
490 self.load_config_file()
487 # enforce cl-opts override configfile opts:
491 # enforce cl-opts override configfile opts:
488 self.update_config(cl_config)
492 self.update_config(cl_config)
@@ -1,312 +1,312 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for managing IPython profiles.
3 An application for managing IPython profiles.
4
4
5 To be invoked as the `ipython profile` subcommand.
5 To be invoked as the `ipython profile` subcommand.
6
6
7 Authors:
7 Authors:
8
8
9 * Min RK
9 * Min RK
10
10
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import os
24 import os
25
25
26 from traitlets.config.application import Application
26 from traitlets.config.application import Application
27 from IPython.core.application import (
27 from IPython.core.application import (
28 BaseIPythonApplication, base_flags
28 BaseIPythonApplication, base_flags
29 )
29 )
30 from IPython.core.profiledir import ProfileDir
30 from IPython.core.profiledir import ProfileDir
31 from IPython.utils.importstring import import_item
31 from IPython.utils.importstring import import_item
32 from IPython.paths import get_ipython_dir, get_ipython_package_dir
32 from IPython.paths import get_ipython_dir, get_ipython_package_dir
33 from traitlets import Unicode, Bool, Dict, observe
33 from traitlets import Unicode, Bool, Dict, observe
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Constants
36 # Constants
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 create_help = """Create an IPython profile by name
39 create_help = """Create an IPython profile by name
40
40
41 Create an ipython profile directory by its name or
41 Create an ipython profile directory by its name or
42 profile directory path. Profile directories contain
42 profile directory path. Profile directories contain
43 configuration, log and security related files and are named
43 configuration, log and security related files and are named
44 using the convention 'profile_<name>'. By default they are
44 using the convention 'profile_<name>'. By default they are
45 located in your ipython directory. Once created, you will
45 located in your ipython directory. Once created, you will
46 can edit the configuration files in the profile
46 can edit the configuration files in the profile
47 directory to configure IPython. Most users will create a
47 directory to configure IPython. Most users will create a
48 profile directory by name,
48 profile directory by name,
49 `ipython profile create myprofile`, which will put the directory
49 `ipython profile create myprofile`, which will put the directory
50 in `<ipython_dir>/profile_myprofile`.
50 in `<ipython_dir>/profile_myprofile`.
51 """
51 """
52 list_help = """List available IPython profiles
52 list_help = """List available IPython profiles
53
53
54 List all available profiles, by profile location, that can
54 List all available profiles, by profile location, that can
55 be found in the current working directly or in the ipython
55 be found in the current working directly or in the ipython
56 directory. Profile directories are named using the convention
56 directory. Profile directories are named using the convention
57 'profile_<profile>'.
57 'profile_<profile>'.
58 """
58 """
59 profile_help = """Manage IPython profiles
59 profile_help = """Manage IPython profiles
60
60
61 Profile directories contain
61 Profile directories contain
62 configuration, log and security related files and are named
62 configuration, log and security related files and are named
63 using the convention 'profile_<name>'. By default they are
63 using the convention 'profile_<name>'. By default they are
64 located in your ipython directory. You can create profiles
64 located in your ipython directory. You can create profiles
65 with `ipython profile create <name>`, or see the profiles you
65 with `ipython profile create <name>`, or see the profiles you
66 already have with `ipython profile list`
66 already have with `ipython profile list`
67
67
68 To get started configuring IPython, simply do:
68 To get started configuring IPython, simply do:
69
69
70 $> ipython profile create
70 $> ipython profile create
71
71
72 and IPython will create the default profile in <ipython_dir>/profile_default,
72 and IPython will create the default profile in <ipython_dir>/profile_default,
73 where you can edit ipython_config.py to start configuring IPython.
73 where you can edit ipython_config.py to start configuring IPython.
74
74
75 """
75 """
76
76
77 _list_examples = "ipython profile list # list all profiles"
77 _list_examples = "ipython profile list # list all profiles"
78
78
79 _create_examples = """
79 _create_examples = """
80 ipython profile create foo # create profile foo w/ default config files
80 ipython profile create foo # create profile foo w/ default config files
81 ipython profile create foo --reset # restage default config files over current
81 ipython profile create foo --reset # restage default config files over current
82 ipython profile create foo --parallel # also stage parallel config files
82 ipython profile create foo --parallel # also stage parallel config files
83 """
83 """
84
84
85 _main_examples = """
85 _main_examples = """
86 ipython profile create -h # show the help string for the create subcommand
86 ipython profile create -h # show the help string for the create subcommand
87 ipython profile list -h # show the help string for the list subcommand
87 ipython profile list -h # show the help string for the list subcommand
88
88
89 ipython locate profile foo # print the path to the directory for profile 'foo'
89 ipython locate profile foo # print the path to the directory for profile 'foo'
90 """
90 """
91
91
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93 # Profile Application Class (for `ipython profile` subcommand)
93 # Profile Application Class (for `ipython profile` subcommand)
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95
95
96
96
97 def list_profiles_in(path):
97 def list_profiles_in(path):
98 """list profiles in a given root directory"""
98 """list profiles in a given root directory"""
99 profiles = []
99 profiles = []
100
100
101 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
101 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
102 files = os.scandir(path)
102 files = os.scandir(path)
103 for f in files:
103 for f in files:
104 if f.is_dir() and f.name.startswith('profile_'):
104 if f.is_dir() and f.name.startswith('profile_'):
105 profiles.append(f.name.split('_', 1)[-1])
105 profiles.append(f.name.split('_', 1)[-1])
106 return profiles
106 return profiles
107
107
108
108
109 def list_bundled_profiles():
109 def list_bundled_profiles():
110 """list profiles that are bundled with IPython."""
110 """list profiles that are bundled with IPython."""
111 path = os.path.join(get_ipython_package_dir(), u'core', u'profile')
111 path = os.path.join(get_ipython_package_dir(), u'core', u'profile')
112 profiles = []
112 profiles = []
113
113
114 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
114 # for python 3.6+ rewrite to: with os.scandir(path) as dirlist:
115 files = os.scandir(path)
115 files = os.scandir(path)
116 for profile in files:
116 for profile in files:
117 if profile.is_dir() and profile.name != "__pycache__":
117 if profile.is_dir() and profile.name != "__pycache__":
118 profiles.append(profile.name)
118 profiles.append(profile.name)
119 return profiles
119 return profiles
120
120
121
121
122 class ProfileLocate(BaseIPythonApplication):
122 class ProfileLocate(BaseIPythonApplication):
123 description = """print the path to an IPython profile dir"""
123 description = """print the path to an IPython profile dir"""
124
124
125 def parse_command_line(self, argv=None):
125 def parse_command_line(self, argv=None):
126 super(ProfileLocate, self).parse_command_line(argv)
126 super(ProfileLocate, self).parse_command_line(argv)
127 if self.extra_args:
127 if self.extra_args:
128 self.profile = self.extra_args[0]
128 self.profile = self.extra_args[0]
129
129
130 def start(self):
130 def start(self):
131 print(self.profile_dir.location)
131 print(self.profile_dir.location)
132
132
133
133
134 class ProfileList(Application):
134 class ProfileList(Application):
135 name = u'ipython-profile'
135 name = u'ipython-profile'
136 description = list_help
136 description = list_help
137 examples = _list_examples
137 examples = _list_examples
138
138
139 aliases = Dict({
139 aliases = Dict({
140 'ipython-dir' : 'ProfileList.ipython_dir',
140 'ipython-dir' : 'ProfileList.ipython_dir',
141 'log-level' : 'Application.log_level',
141 'log-level' : 'Application.log_level',
142 })
142 })
143 flags = Dict(dict(
143 flags = Dict(dict(
144 debug = ({'Application' : {'log_level' : 0}},
144 debug = ({'Application' : {'log_level' : 0}},
145 "Set Application.log_level to 0, maximizing log output."
145 "Set Application.log_level to 0, maximizing log output."
146 )
146 )
147 ))
147 ))
148
148
149 ipython_dir = Unicode(get_ipython_dir(),
149 ipython_dir = Unicode(get_ipython_dir(),
150 help="""
150 help="""
151 The name of the IPython directory. This directory is used for logging
151 The name of the IPython directory. This directory is used for logging
152 configuration (through profiles), history storage, etc. The default
152 configuration (through profiles), history storage, etc. The default
153 is usually $HOME/.ipython. This options can also be specified through
153 is usually $HOME/.ipython. This options can also be specified through
154 the environment variable IPYTHONDIR.
154 the environment variable IPYTHONDIR.
155 """
155 """
156 ).tag(config=True)
156 ).tag(config=True)
157
157
158
158
159 def _print_profiles(self, profiles):
159 def _print_profiles(self, profiles):
160 """print list of profiles, indented."""
160 """print list of profiles, indented."""
161 for profile in profiles:
161 for profile in profiles:
162 print(' %s' % profile)
162 print(' %s' % profile)
163
163
164 def list_profile_dirs(self):
164 def list_profile_dirs(self):
165 profiles = list_bundled_profiles()
165 profiles = list_bundled_profiles()
166 if profiles:
166 if profiles:
167 print()
167 print()
168 print("Available profiles in IPython:")
168 print("Available profiles in IPython:")
169 self._print_profiles(profiles)
169 self._print_profiles(profiles)
170 print()
170 print()
171 print(" The first request for a bundled profile will copy it")
171 print(" The first request for a bundled profile will copy it")
172 print(" into your IPython directory (%s)," % self.ipython_dir)
172 print(" into your IPython directory (%s)," % self.ipython_dir)
173 print(" where you can customize it.")
173 print(" where you can customize it.")
174
174
175 profiles = list_profiles_in(self.ipython_dir)
175 profiles = list_profiles_in(self.ipython_dir)
176 if profiles:
176 if profiles:
177 print()
177 print()
178 print("Available profiles in %s:" % self.ipython_dir)
178 print("Available profiles in %s:" % self.ipython_dir)
179 self._print_profiles(profiles)
179 self._print_profiles(profiles)
180
180
181 profiles = list_profiles_in(os.getcwd())
181 profiles = list_profiles_in(os.getcwd())
182 if profiles:
182 if profiles:
183 print()
183 print()
184 print(
184 print(
185 "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
185 "Profiles from CWD have been removed for security reason, see CVE-2022-21699:"
186 )
186 )
187
187
188 print()
188 print()
189 print("To use any of the above profiles, start IPython with:")
189 print("To use any of the above profiles, start IPython with:")
190 print(" ipython --profile=<name>")
190 print(" ipython --profile=<name>")
191 print()
191 print()
192
192
193 def start(self):
193 def start(self):
194 self.list_profile_dirs()
194 self.list_profile_dirs()
195
195
196
196
197 create_flags = {}
197 create_flags = {}
198 create_flags.update(base_flags)
198 create_flags.update(base_flags)
199 # don't include '--init' flag, which implies running profile create in other apps
199 # don't include '--init' flag, which implies running profile create in other apps
200 create_flags.pop('init')
200 create_flags.pop('init')
201 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
201 create_flags['reset'] = ({'ProfileCreate': {'overwrite' : True}},
202 "reset config files in this profile to the defaults.")
202 "reset config files in this profile to the defaults.")
203 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
203 create_flags['parallel'] = ({'ProfileCreate': {'parallel' : True}},
204 "Include the config files for parallel "
204 "Include the config files for parallel "
205 "computing apps (ipengine, ipcontroller, etc.)")
205 "computing apps (ipengine, ipcontroller, etc.)")
206
206
207
207
208 class ProfileCreate(BaseIPythonApplication):
208 class ProfileCreate(BaseIPythonApplication):
209 name = u'ipython-profile'
209 name = u'ipython-profile'
210 description = create_help
210 description = create_help
211 examples = _create_examples
211 examples = _create_examples
212 auto_create = Bool(True)
212 auto_create = Bool(True).tag(config=True)
213 def _log_format_default(self):
213 def _log_format_default(self):
214 return "[%(name)s] %(message)s"
214 return "[%(name)s] %(message)s"
215
215
216 def _copy_config_files_default(self):
216 def _copy_config_files_default(self):
217 return True
217 return True
218
218
219 parallel = Bool(False,
219 parallel = Bool(False,
220 help="whether to include parallel computing config files"
220 help="whether to include parallel computing config files"
221 ).tag(config=True)
221 ).tag(config=True)
222
222
223 @observe('parallel')
223 @observe('parallel')
224 def _parallel_changed(self, change):
224 def _parallel_changed(self, change):
225 parallel_files = [ 'ipcontroller_config.py',
225 parallel_files = [ 'ipcontroller_config.py',
226 'ipengine_config.py',
226 'ipengine_config.py',
227 'ipcluster_config.py'
227 'ipcluster_config.py'
228 ]
228 ]
229 if change['new']:
229 if change['new']:
230 for cf in parallel_files:
230 for cf in parallel_files:
231 self.config_files.append(cf)
231 self.config_files.append(cf)
232 else:
232 else:
233 for cf in parallel_files:
233 for cf in parallel_files:
234 if cf in self.config_files:
234 if cf in self.config_files:
235 self.config_files.remove(cf)
235 self.config_files.remove(cf)
236
236
237 def parse_command_line(self, argv):
237 def parse_command_line(self, argv):
238 super(ProfileCreate, self).parse_command_line(argv)
238 super(ProfileCreate, self).parse_command_line(argv)
239 # accept positional arg as profile name
239 # accept positional arg as profile name
240 if self.extra_args:
240 if self.extra_args:
241 self.profile = self.extra_args[0]
241 self.profile = self.extra_args[0]
242
242
243 flags = Dict(create_flags)
243 flags = Dict(create_flags)
244
244
245 classes = [ProfileDir]
245 classes = [ProfileDir]
246
246
247 def _import_app(self, app_path):
247 def _import_app(self, app_path):
248 """import an app class"""
248 """import an app class"""
249 app = None
249 app = None
250 name = app_path.rsplit('.', 1)[-1]
250 name = app_path.rsplit('.', 1)[-1]
251 try:
251 try:
252 app = import_item(app_path)
252 app = import_item(app_path)
253 except ImportError:
253 except ImportError:
254 self.log.info("Couldn't import %s, config file will be excluded", name)
254 self.log.info("Couldn't import %s, config file will be excluded", name)
255 except Exception:
255 except Exception:
256 self.log.warning('Unexpected error importing %s', name, exc_info=True)
256 self.log.warning('Unexpected error importing %s', name, exc_info=True)
257 return app
257 return app
258
258
259 def init_config_files(self):
259 def init_config_files(self):
260 super(ProfileCreate, self).init_config_files()
260 super(ProfileCreate, self).init_config_files()
261 # use local imports, since these classes may import from here
261 # use local imports, since these classes may import from here
262 from IPython.terminal.ipapp import TerminalIPythonApp
262 from IPython.terminal.ipapp import TerminalIPythonApp
263 apps = [TerminalIPythonApp]
263 apps = [TerminalIPythonApp]
264 for app_path in (
264 for app_path in (
265 'ipykernel.kernelapp.IPKernelApp',
265 'ipykernel.kernelapp.IPKernelApp',
266 ):
266 ):
267 app = self._import_app(app_path)
267 app = self._import_app(app_path)
268 if app is not None:
268 if app is not None:
269 apps.append(app)
269 apps.append(app)
270 if self.parallel:
270 if self.parallel:
271 from ipyparallel.apps.ipcontrollerapp import IPControllerApp
271 from ipyparallel.apps.ipcontrollerapp import IPControllerApp
272 from ipyparallel.apps.ipengineapp import IPEngineApp
272 from ipyparallel.apps.ipengineapp import IPEngineApp
273 from ipyparallel.apps.ipclusterapp import IPClusterStart
273 from ipyparallel.apps.ipclusterapp import IPClusterStart
274 apps.extend([
274 apps.extend([
275 IPControllerApp,
275 IPControllerApp,
276 IPEngineApp,
276 IPEngineApp,
277 IPClusterStart,
277 IPClusterStart,
278 ])
278 ])
279 for App in apps:
279 for App in apps:
280 app = App()
280 app = App()
281 app.config.update(self.config)
281 app.config.update(self.config)
282 app.log = self.log
282 app.log = self.log
283 app.overwrite = self.overwrite
283 app.overwrite = self.overwrite
284 app.copy_config_files=True
284 app.copy_config_files=True
285 app.ipython_dir=self.ipython_dir
285 app.ipython_dir=self.ipython_dir
286 app.profile_dir=self.profile_dir
286 app.profile_dir=self.profile_dir
287 app.init_config_files()
287 app.init_config_files()
288
288
289 def stage_default_config_file(self):
289 def stage_default_config_file(self):
290 pass
290 pass
291
291
292
292
293 class ProfileApp(Application):
293 class ProfileApp(Application):
294 name = u'ipython profile'
294 name = u'ipython profile'
295 description = profile_help
295 description = profile_help
296 examples = _main_examples
296 examples = _main_examples
297
297
298 subcommands = Dict(dict(
298 subcommands = Dict(dict(
299 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
299 create = (ProfileCreate, ProfileCreate.description.splitlines()[0]),
300 list = (ProfileList, ProfileList.description.splitlines()[0]),
300 list = (ProfileList, ProfileList.description.splitlines()[0]),
301 locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]),
301 locate = (ProfileLocate, ProfileLocate.description.splitlines()[0]),
302 ))
302 ))
303
303
304 def start(self):
304 def start(self):
305 if self.subapp is None:
305 if self.subapp is None:
306 print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys()))
306 print("No subcommand specified. Must specify one of: %s"%(self.subcommands.keys()))
307 print()
307 print()
308 self.print_description()
308 self.print_description()
309 self.print_subcommands()
309 self.print_subcommands()
310 self.exit(1)
310 self.exit(1)
311 else:
311 else:
312 return self.subapp.start()
312 return self.subapp.start()
@@ -1,338 +1,338 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 The :class:`~traitlets.config.application.Application` object for the command
3 The :class:`~traitlets.config.application.Application` object for the command
4 line :command:`ipython` program.
4 line :command:`ipython` program.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10
10
11 import logging
11 import logging
12 import os
12 import os
13 import sys
13 import sys
14 import warnings
14 import warnings
15
15
16 from traitlets.config.loader import Config
16 from traitlets.config.loader import Config
17 from traitlets.config.application import boolean_flag, catch_config_error
17 from traitlets.config.application import boolean_flag, catch_config_error
18 from IPython.core import release
18 from IPython.core import release
19 from IPython.core import usage
19 from IPython.core import usage
20 from IPython.core.completer import IPCompleter
20 from IPython.core.completer import IPCompleter
21 from IPython.core.crashhandler import CrashHandler
21 from IPython.core.crashhandler import CrashHandler
22 from IPython.core.formatters import PlainTextFormatter
22 from IPython.core.formatters import PlainTextFormatter
23 from IPython.core.history import HistoryManager
23 from IPython.core.history import HistoryManager
24 from IPython.core.application import (
24 from IPython.core.application import (
25 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
25 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
26 )
26 )
27 from IPython.core.magic import MagicsManager
27 from IPython.core.magic import MagicsManager
28 from IPython.core.magics import (
28 from IPython.core.magics import (
29 ScriptMagics, LoggingMagics
29 ScriptMagics, LoggingMagics
30 )
30 )
31 from IPython.core.shellapp import (
31 from IPython.core.shellapp import (
32 InteractiveShellApp, shell_flags, shell_aliases
32 InteractiveShellApp, shell_flags, shell_aliases
33 )
33 )
34 from IPython.extensions.storemagic import StoreMagics
34 from IPython.extensions.storemagic import StoreMagics
35 from .interactiveshell import TerminalInteractiveShell
35 from .interactiveshell import TerminalInteractiveShell
36 from IPython.paths import get_ipython_dir
36 from IPython.paths import get_ipython_dir
37 from traitlets import (
37 from traitlets import (
38 Bool, List, default, observe, Type
38 Bool, List, default, observe, Type
39 )
39 )
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Globals, utilities and helpers
42 # Globals, utilities and helpers
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45 _examples = """
45 _examples = """
46 ipython --matplotlib # enable matplotlib integration
46 ipython --matplotlib # enable matplotlib integration
47 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
47 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
48
48
49 ipython --log-level=DEBUG # set logging to DEBUG
49 ipython --log-level=DEBUG # set logging to DEBUG
50 ipython --profile=foo # start with profile foo
50 ipython --profile=foo # start with profile foo
51
51
52 ipython profile create foo # create profile foo w/ default config files
52 ipython profile create foo # create profile foo w/ default config files
53 ipython help profile # show the help for the profile subcmd
53 ipython help profile # show the help for the profile subcmd
54
54
55 ipython locate # print the path to the IPython directory
55 ipython locate # print the path to the IPython directory
56 ipython locate profile foo # print the path to the directory for profile `foo`
56 ipython locate profile foo # print the path to the directory for profile `foo`
57 """
57 """
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Crash handler for this application
60 # Crash handler for this application
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63 class IPAppCrashHandler(CrashHandler):
63 class IPAppCrashHandler(CrashHandler):
64 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
64 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
65
65
66 def __init__(self, app):
66 def __init__(self, app):
67 contact_name = release.author
67 contact_name = release.author
68 contact_email = release.author_email
68 contact_email = release.author_email
69 bug_tracker = 'https://github.com/ipython/ipython/issues'
69 bug_tracker = 'https://github.com/ipython/ipython/issues'
70 super(IPAppCrashHandler,self).__init__(
70 super(IPAppCrashHandler,self).__init__(
71 app, contact_name, contact_email, bug_tracker
71 app, contact_name, contact_email, bug_tracker
72 )
72 )
73
73
74 def make_report(self,traceback):
74 def make_report(self,traceback):
75 """Return a string containing a crash report."""
75 """Return a string containing a crash report."""
76
76
77 sec_sep = self.section_sep
77 sec_sep = self.section_sep
78 # Start with parent report
78 # Start with parent report
79 report = [super(IPAppCrashHandler, self).make_report(traceback)]
79 report = [super(IPAppCrashHandler, self).make_report(traceback)]
80 # Add interactive-specific info we may have
80 # Add interactive-specific info we may have
81 rpt_add = report.append
81 rpt_add = report.append
82 try:
82 try:
83 rpt_add(sec_sep+"History of session input:")
83 rpt_add(sec_sep+"History of session input:")
84 for line in self.app.shell.user_ns['_ih']:
84 for line in self.app.shell.user_ns['_ih']:
85 rpt_add(line)
85 rpt_add(line)
86 rpt_add('\n*** Last line of input (may not be in above history):\n')
86 rpt_add('\n*** Last line of input (may not be in above history):\n')
87 rpt_add(self.app.shell._last_input_line+'\n')
87 rpt_add(self.app.shell._last_input_line+'\n')
88 except:
88 except:
89 pass
89 pass
90
90
91 return ''.join(report)
91 return ''.join(report)
92
92
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94 # Aliases and Flags
94 # Aliases and Flags
95 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
96 flags = dict(base_flags)
96 flags = dict(base_flags)
97 flags.update(shell_flags)
97 flags.update(shell_flags)
98 frontend_flags = {}
98 frontend_flags = {}
99 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
99 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
100 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
100 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
101 'Turn on auto editing of files with syntax errors.',
101 'Turn on auto editing of files with syntax errors.',
102 'Turn off auto editing of files with syntax errors.'
102 'Turn off auto editing of files with syntax errors.'
103 )
103 )
104 addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
104 addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
105 "Force simple minimal prompt using `raw_input`",
105 "Force simple minimal prompt using `raw_input`",
106 "Use a rich interactive prompt with prompt_toolkit",
106 "Use a rich interactive prompt with prompt_toolkit",
107 )
107 )
108
108
109 addflag('banner', 'TerminalIPythonApp.display_banner',
109 addflag('banner', 'TerminalIPythonApp.display_banner',
110 "Display a banner upon starting IPython.",
110 "Display a banner upon starting IPython.",
111 "Don't display a banner upon starting IPython."
111 "Don't display a banner upon starting IPython."
112 )
112 )
113 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
113 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
114 """Set to confirm when you try to exit IPython with an EOF (Control-D
114 """Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
116 you can force a direct exit without any confirmation.""",
117 "Don't prompt the user when exiting."
117 "Don't prompt the user when exiting."
118 )
118 )
119 addflag('term-title', 'TerminalInteractiveShell.term_title',
119 addflag('term-title', 'TerminalInteractiveShell.term_title',
120 "Enable auto setting the terminal title.",
120 "Enable auto setting the terminal title.",
121 "Disable auto setting the terminal title."
121 "Disable auto setting the terminal title."
122 )
122 )
123 classic_config = Config()
123 classic_config = Config()
124 classic_config.InteractiveShell.cache_size = 0
124 classic_config.InteractiveShell.cache_size = 0
125 classic_config.PlainTextFormatter.pprint = False
125 classic_config.PlainTextFormatter.pprint = False
126 classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts'
126 classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts'
127 classic_config.InteractiveShell.separate_in = ''
127 classic_config.InteractiveShell.separate_in = ''
128 classic_config.InteractiveShell.separate_out = ''
128 classic_config.InteractiveShell.separate_out = ''
129 classic_config.InteractiveShell.separate_out2 = ''
129 classic_config.InteractiveShell.separate_out2 = ''
130 classic_config.InteractiveShell.colors = 'NoColor'
130 classic_config.InteractiveShell.colors = 'NoColor'
131 classic_config.InteractiveShell.xmode = 'Plain'
131 classic_config.InteractiveShell.xmode = 'Plain'
132
132
133 frontend_flags['classic']=(
133 frontend_flags['classic']=(
134 classic_config,
134 classic_config,
135 "Gives IPython a similar feel to the classic Python prompt."
135 "Gives IPython a similar feel to the classic Python prompt."
136 )
136 )
137 # # log doesn't make so much sense this way anymore
137 # # log doesn't make so much sense this way anymore
138 # paa('--log','-l',
138 # paa('--log','-l',
139 # action='store_true', dest='InteractiveShell.logstart',
139 # action='store_true', dest='InteractiveShell.logstart',
140 # help="Start logging to the default log file (./ipython_log.py).")
140 # help="Start logging to the default log file (./ipython_log.py).")
141 #
141 #
142 # # quick is harder to implement
142 # # quick is harder to implement
143 frontend_flags['quick']=(
143 frontend_flags['quick']=(
144 {'TerminalIPythonApp' : {'quick' : True}},
144 {'TerminalIPythonApp' : {'quick' : True}},
145 "Enable quick startup with no config files."
145 "Enable quick startup with no config files."
146 )
146 )
147
147
148 frontend_flags['i'] = (
148 frontend_flags['i'] = (
149 {'TerminalIPythonApp' : {'force_interact' : True}},
149 {'TerminalIPythonApp' : {'force_interact' : True}},
150 """If running code from the command line, become interactive afterwards.
150 """If running code from the command line, become interactive afterwards.
151 It is often useful to follow this with `--` to treat remaining flags as
151 It is often useful to follow this with `--` to treat remaining flags as
152 script arguments.
152 script arguments.
153 """
153 """
154 )
154 )
155 flags.update(frontend_flags)
155 flags.update(frontend_flags)
156
156
157 aliases = dict(base_aliases)
157 aliases = dict(base_aliases)
158 aliases.update(shell_aliases) # type: ignore[arg-type]
158 aliases.update(shell_aliases) # type: ignore[arg-type]
159
159
160 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
161 # Main classes and functions
161 # Main classes and functions
162 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
163
163
164
164
165 class LocateIPythonApp(BaseIPythonApplication):
165 class LocateIPythonApp(BaseIPythonApplication):
166 description = """print the path to the IPython dir"""
166 description = """print the path to the IPython dir"""
167 subcommands = dict(
167 subcommands = dict(
168 profile=('IPython.core.profileapp.ProfileLocate',
168 profile=('IPython.core.profileapp.ProfileLocate',
169 "print the path to an IPython profile directory",
169 "print the path to an IPython profile directory",
170 ),
170 ),
171 )
171 )
172 def start(self):
172 def start(self):
173 if self.subapp is not None:
173 if self.subapp is not None:
174 return self.subapp.start()
174 return self.subapp.start()
175 else:
175 else:
176 print(self.ipython_dir)
176 print(self.ipython_dir)
177
177
178
178
179 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
179 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
180 name = u'ipython'
180 name = "ipython"
181 description = usage.cl_usage
181 description = usage.cl_usage
182 crash_handler_class = IPAppCrashHandler # typing: ignore[assignment]
182 crash_handler_class = IPAppCrashHandler # typing: ignore[assignment]
183 examples = _examples
183 examples = _examples
184
184
185 flags = flags
185 flags = flags
186 aliases = aliases
186 aliases = aliases
187 classes = List()
187 classes = List()
188
188
189 interactive_shell_class = Type(
189 interactive_shell_class = Type(
190 klass=object, # use default_value otherwise which only allow subclasses.
190 klass=object, # use default_value otherwise which only allow subclasses.
191 default_value=TerminalInteractiveShell,
191 default_value=TerminalInteractiveShell,
192 help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
192 help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
193 ).tag(config=True)
193 ).tag(config=True)
194
194
195 @default('classes')
195 @default('classes')
196 def _classes_default(self):
196 def _classes_default(self):
197 """This has to be in a method, for TerminalIPythonApp to be available."""
197 """This has to be in a method, for TerminalIPythonApp to be available."""
198 return [
198 return [
199 InteractiveShellApp, # ShellApp comes before TerminalApp, because
199 InteractiveShellApp, # ShellApp comes before TerminalApp, because
200 self.__class__, # it will also affect subclasses (e.g. QtConsole)
200 self.__class__, # it will also affect subclasses (e.g. QtConsole)
201 TerminalInteractiveShell,
201 TerminalInteractiveShell,
202 HistoryManager,
202 HistoryManager,
203 MagicsManager,
203 MagicsManager,
204 ProfileDir,
204 ProfileDir,
205 PlainTextFormatter,
205 PlainTextFormatter,
206 IPCompleter,
206 IPCompleter,
207 ScriptMagics,
207 ScriptMagics,
208 LoggingMagics,
208 LoggingMagics,
209 StoreMagics,
209 StoreMagics,
210 ]
210 ]
211
211
212 subcommands = dict(
212 subcommands = dict(
213 profile = ("IPython.core.profileapp.ProfileApp",
213 profile = ("IPython.core.profileapp.ProfileApp",
214 "Create and manage IPython profiles."
214 "Create and manage IPython profiles."
215 ),
215 ),
216 kernel = ("ipykernel.kernelapp.IPKernelApp",
216 kernel = ("ipykernel.kernelapp.IPKernelApp",
217 "Start a kernel without an attached frontend."
217 "Start a kernel without an attached frontend."
218 ),
218 ),
219 locate=('IPython.terminal.ipapp.LocateIPythonApp',
219 locate=('IPython.terminal.ipapp.LocateIPythonApp',
220 LocateIPythonApp.description
220 LocateIPythonApp.description
221 ),
221 ),
222 history=('IPython.core.historyapp.HistoryApp',
222 history=('IPython.core.historyapp.HistoryApp',
223 "Manage the IPython history database."
223 "Manage the IPython history database."
224 ),
224 ),
225 )
225 )
226
226
227
228 # *do* autocreate requested profile, but don't create the config file.
227 # *do* autocreate requested profile, but don't create the config file.
229 auto_create=Bool(True)
228 auto_create = Bool(True).tag(config=True)
229
230 # configurables
230 # configurables
231 quick = Bool(False,
231 quick = Bool(False,
232 help="""Start IPython quickly by skipping the loading of config files."""
232 help="""Start IPython quickly by skipping the loading of config files."""
233 ).tag(config=True)
233 ).tag(config=True)
234 @observe('quick')
234 @observe('quick')
235 def _quick_changed(self, change):
235 def _quick_changed(self, change):
236 if change['new']:
236 if change['new']:
237 self.load_config_file = lambda *a, **kw: None
237 self.load_config_file = lambda *a, **kw: None
238
238
239 display_banner = Bool(True,
239 display_banner = Bool(True,
240 help="Whether to display a banner upon starting IPython."
240 help="Whether to display a banner upon starting IPython."
241 ).tag(config=True)
241 ).tag(config=True)
242
242
243 # if there is code of files to run from the cmd line, don't interact
243 # if there is code of files to run from the cmd line, don't interact
244 # unless the --i flag (App.force_interact) is true.
244 # unless the --i flag (App.force_interact) is true.
245 force_interact = Bool(False,
245 force_interact = Bool(False,
246 help="""If a command or file is given via the command-line,
246 help="""If a command or file is given via the command-line,
247 e.g. 'ipython foo.py', start an interactive shell after executing the
247 e.g. 'ipython foo.py', start an interactive shell after executing the
248 file or command."""
248 file or command."""
249 ).tag(config=True)
249 ).tag(config=True)
250 @observe('force_interact')
250 @observe('force_interact')
251 def _force_interact_changed(self, change):
251 def _force_interact_changed(self, change):
252 if change['new']:
252 if change['new']:
253 self.interact = True
253 self.interact = True
254
254
255 @observe('file_to_run', 'code_to_run', 'module_to_run')
255 @observe('file_to_run', 'code_to_run', 'module_to_run')
256 def _file_to_run_changed(self, change):
256 def _file_to_run_changed(self, change):
257 new = change['new']
257 new = change['new']
258 if new:
258 if new:
259 self.something_to_run = True
259 self.something_to_run = True
260 if new and not self.force_interact:
260 if new and not self.force_interact:
261 self.interact = False
261 self.interact = False
262
262
263 # internal, not-configurable
263 # internal, not-configurable
264 something_to_run=Bool(False)
264 something_to_run=Bool(False)
265
265
266 @catch_config_error
266 @catch_config_error
267 def initialize(self, argv=None):
267 def initialize(self, argv=None):
268 """Do actions after construct, but before starting the app."""
268 """Do actions after construct, but before starting the app."""
269 super(TerminalIPythonApp, self).initialize(argv)
269 super(TerminalIPythonApp, self).initialize(argv)
270 if self.subapp is not None:
270 if self.subapp is not None:
271 # don't bother initializing further, starting subapp
271 # don't bother initializing further, starting subapp
272 return
272 return
273 # print self.extra_args
273 # print self.extra_args
274 if self.extra_args and not self.something_to_run:
274 if self.extra_args and not self.something_to_run:
275 self.file_to_run = self.extra_args[0]
275 self.file_to_run = self.extra_args[0]
276 self.init_path()
276 self.init_path()
277 # create the shell
277 # create the shell
278 self.init_shell()
278 self.init_shell()
279 # and draw the banner
279 # and draw the banner
280 self.init_banner()
280 self.init_banner()
281 # Now a variety of things that happen after the banner is printed.
281 # Now a variety of things that happen after the banner is printed.
282 self.init_gui_pylab()
282 self.init_gui_pylab()
283 self.init_extensions()
283 self.init_extensions()
284 self.init_code()
284 self.init_code()
285
285
286 def init_shell(self):
286 def init_shell(self):
287 """initialize the InteractiveShell instance"""
287 """initialize the InteractiveShell instance"""
288 # Create an InteractiveShell instance.
288 # Create an InteractiveShell instance.
289 # shell.display_banner should always be False for the terminal
289 # shell.display_banner should always be False for the terminal
290 # based app, because we call shell.show_banner() by hand below
290 # based app, because we call shell.show_banner() by hand below
291 # so the banner shows *before* all extension loading stuff.
291 # so the banner shows *before* all extension loading stuff.
292 self.shell = self.interactive_shell_class.instance(parent=self,
292 self.shell = self.interactive_shell_class.instance(parent=self,
293 profile_dir=self.profile_dir,
293 profile_dir=self.profile_dir,
294 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
294 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
295 self.shell.configurables.append(self)
295 self.shell.configurables.append(self)
296
296
297 def init_banner(self):
297 def init_banner(self):
298 """optionally display the banner"""
298 """optionally display the banner"""
299 if self.display_banner and self.interact:
299 if self.display_banner and self.interact:
300 self.shell.show_banner()
300 self.shell.show_banner()
301 # Make sure there is a space below the banner.
301 # Make sure there is a space below the banner.
302 if self.log_level <= logging.INFO: print()
302 if self.log_level <= logging.INFO: print()
303
303
304 def _pylab_changed(self, name, old, new):
304 def _pylab_changed(self, name, old, new):
305 """Replace --pylab='inline' with --pylab='auto'"""
305 """Replace --pylab='inline' with --pylab='auto'"""
306 if new == 'inline':
306 if new == 'inline':
307 warnings.warn("'inline' not available as pylab backend, "
307 warnings.warn("'inline' not available as pylab backend, "
308 "using 'auto' instead.")
308 "using 'auto' instead.")
309 self.pylab = 'auto'
309 self.pylab = 'auto'
310
310
311 def start(self):
311 def start(self):
312 if self.subapp is not None:
312 if self.subapp is not None:
313 return self.subapp.start()
313 return self.subapp.start()
314 # perform any prexec steps:
314 # perform any prexec steps:
315 if self.interact:
315 if self.interact:
316 self.log.debug("Starting IPython's mainloop...")
316 self.log.debug("Starting IPython's mainloop...")
317 self.shell.mainloop()
317 self.shell.mainloop()
318 else:
318 else:
319 self.log.debug("IPython not interactive...")
319 self.log.debug("IPython not interactive...")
320 self.shell.restore_term_title()
320 self.shell.restore_term_title()
321 if not self.shell.last_execution_succeeded:
321 if not self.shell.last_execution_succeeded:
322 sys.exit(1)
322 sys.exit(1)
323
323
324 def load_default_config(ipython_dir=None):
324 def load_default_config(ipython_dir=None):
325 """Load the default config file from the default ipython_dir.
325 """Load the default config file from the default ipython_dir.
326
326
327 This is useful for embedded shells.
327 This is useful for embedded shells.
328 """
328 """
329 if ipython_dir is None:
329 if ipython_dir is None:
330 ipython_dir = get_ipython_dir()
330 ipython_dir = get_ipython_dir()
331
331
332 profile_dir = os.path.join(ipython_dir, 'profile_default')
332 profile_dir = os.path.join(ipython_dir, 'profile_default')
333 app = TerminalIPythonApp()
333 app = TerminalIPythonApp()
334 app.config_file_paths.append(profile_dir)
334 app.config_file_paths.append(profile_dir)
335 app.load_config_file()
335 app.load_config_file()
336 return app.config
336 return app.config
337
337
338 launch_new_instance = TerminalIPythonApp.launch_instance
338 launch_new_instance = TerminalIPythonApp.launch_instance
General Comments 0
You need to be logged in to leave comments. Login now