##// END OF EJS Templates
Merge pull request #13198 from minrk/profiledir-full-log...
Matthias Bussonnier -
r26863:5aabdc16 merge
parent child Browse files
Show More
@@ -1,486 +1,486 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 glob
17 import glob
18 import logging
18 import logging
19 import os
19 import os
20 import shutil
20 import shutil
21 import sys
21 import sys
22
22
23 from pathlib import Path
23 from pathlib import Path
24
24
25 from traitlets.config.application import Application, catch_config_error
25 from traitlets.config.application import Application, catch_config_error
26 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
26 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
27 from IPython.core import release, crashhandler
27 from IPython.core import release, crashhandler
28 from IPython.core.profiledir import ProfileDir, ProfileDirError
28 from IPython.core.profiledir import ProfileDir, ProfileDirError
29 from IPython.paths import get_ipython_dir, get_ipython_package_dir
29 from IPython.paths import get_ipython_dir, get_ipython_package_dir
30 from IPython.utils.path import ensure_dir_exists
30 from IPython.utils.path import ensure_dir_exists
31 from traitlets import (
31 from traitlets import (
32 List, Unicode, Type, Bool, Set, Instance, Undefined,
32 List, Unicode, Type, Bool, Set, Instance, Undefined,
33 default, observe,
33 default, observe,
34 )
34 )
35
35
36 if os.name == "nt":
36 if os.name == "nt":
37 programdata = Path(os.environ.get("PROGRAMDATA", None))
37 programdata = Path(os.environ.get("PROGRAMDATA", None))
38 if programdata:
38 if programdata:
39 SYSTEM_CONFIG_DIRS = [str(programdata / "ipython")]
39 SYSTEM_CONFIG_DIRS = [str(programdata / "ipython")]
40 else: # PROGRAMDATA is not defined by default on XP.
40 else: # PROGRAMDATA is not defined by default on XP.
41 SYSTEM_CONFIG_DIRS = []
41 SYSTEM_CONFIG_DIRS = []
42 else:
42 else:
43 SYSTEM_CONFIG_DIRS = [
43 SYSTEM_CONFIG_DIRS = [
44 "/usr/local/etc/ipython",
44 "/usr/local/etc/ipython",
45 "/etc/ipython",
45 "/etc/ipython",
46 ]
46 ]
47
47
48
48
49 ENV_CONFIG_DIRS = []
49 ENV_CONFIG_DIRS = []
50 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
50 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
51 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
51 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
52 # only add ENV_CONFIG if sys.prefix is not already included
52 # only add ENV_CONFIG if sys.prefix is not already included
53 ENV_CONFIG_DIRS.append(_env_config_dir)
53 ENV_CONFIG_DIRS.append(_env_config_dir)
54
54
55
55
56 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
56 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
57 if _envvar in {None, ''}:
57 if _envvar in {None, ''}:
58 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
58 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
59 else:
59 else:
60 if _envvar.lower() in {'1','true'}:
60 if _envvar.lower() in {'1','true'}:
61 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
61 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
62 elif _envvar.lower() in {'0','false'} :
62 elif _envvar.lower() in {'0','false'} :
63 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
63 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
64 else:
64 else:
65 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 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
66
66
67 # aliases and flags
67 # aliases and flags
68
68
69 base_aliases = {}
69 base_aliases = {}
70 if isinstance(Application.aliases, dict):
70 if isinstance(Application.aliases, dict):
71 # traitlets 5
71 # traitlets 5
72 base_aliases.update(Application.aliases)
72 base_aliases.update(Application.aliases)
73 base_aliases.update(
73 base_aliases.update(
74 {
74 {
75 "profile-dir": "ProfileDir.location",
75 "profile-dir": "ProfileDir.location",
76 "profile": "BaseIPythonApplication.profile",
76 "profile": "BaseIPythonApplication.profile",
77 "ipython-dir": "BaseIPythonApplication.ipython_dir",
77 "ipython-dir": "BaseIPythonApplication.ipython_dir",
78 "log-level": "Application.log_level",
78 "log-level": "Application.log_level",
79 "config": "BaseIPythonApplication.extra_config_file",
79 "config": "BaseIPythonApplication.extra_config_file",
80 }
80 }
81 )
81 )
82
82
83 base_flags = dict()
83 base_flags = dict()
84 if isinstance(Application.flags, dict):
84 if isinstance(Application.flags, dict):
85 # traitlets 5
85 # traitlets 5
86 base_flags.update(Application.flags)
86 base_flags.update(Application.flags)
87 base_flags.update(
87 base_flags.update(
88 dict(
88 dict(
89 debug=(
89 debug=(
90 {"Application": {"log_level": logging.DEBUG}},
90 {"Application": {"log_level": logging.DEBUG}},
91 "set log level to logging.DEBUG (maximize logging output)",
91 "set log level to logging.DEBUG (maximize logging output)",
92 ),
92 ),
93 quiet=(
93 quiet=(
94 {"Application": {"log_level": logging.CRITICAL}},
94 {"Application": {"log_level": logging.CRITICAL}},
95 "set log level to logging.CRITICAL (minimize logging output)",
95 "set log level to logging.CRITICAL (minimize logging output)",
96 ),
96 ),
97 init=(
97 init=(
98 {
98 {
99 "BaseIPythonApplication": {
99 "BaseIPythonApplication": {
100 "copy_config_files": True,
100 "copy_config_files": True,
101 "auto_create": True,
101 "auto_create": True,
102 }
102 }
103 },
103 },
104 """Initialize profile with default config files. This is equivalent
104 """Initialize profile with default config files. This is equivalent
105 to running `ipython profile create <profile>` prior to startup.
105 to running `ipython profile create <profile>` prior to startup.
106 """,
106 """,
107 ),
107 ),
108 )
108 )
109 )
109 )
110
110
111
111
112 class ProfileAwareConfigLoader(PyFileConfigLoader):
112 class ProfileAwareConfigLoader(PyFileConfigLoader):
113 """A Python file config loader that is aware of IPython profiles."""
113 """A Python file config loader that is aware of IPython profiles."""
114 def load_subconfig(self, fname, path=None, profile=None):
114 def load_subconfig(self, fname, path=None, profile=None):
115 if profile is not None:
115 if profile is not None:
116 try:
116 try:
117 profile_dir = ProfileDir.find_profile_dir_by_name(
117 profile_dir = ProfileDir.find_profile_dir_by_name(
118 get_ipython_dir(),
118 get_ipython_dir(),
119 profile,
119 profile,
120 )
120 )
121 except ProfileDirError:
121 except ProfileDirError:
122 return
122 return
123 path = profile_dir.location
123 path = profile_dir.location
124 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
124 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
125
125
126 class BaseIPythonApplication(Application):
126 class BaseIPythonApplication(Application):
127
127
128 name = u'ipython'
128 name = u'ipython'
129 description = Unicode(u'IPython: an enhanced interactive Python shell.')
129 description = Unicode(u'IPython: an enhanced interactive Python shell.')
130 version = Unicode(release.version)
130 version = Unicode(release.version)
131
131
132 aliases = base_aliases
132 aliases = base_aliases
133 flags = base_flags
133 flags = base_flags
134 classes = List([ProfileDir])
134 classes = List([ProfileDir])
135
135
136 # enable `load_subconfig('cfg.py', profile='name')`
136 # enable `load_subconfig('cfg.py', profile='name')`
137 python_config_loader_class = ProfileAwareConfigLoader
137 python_config_loader_class = ProfileAwareConfigLoader
138
138
139 # Track whether the config_file has changed,
139 # Track whether the config_file has changed,
140 # because some logic happens only if we aren't using the default.
140 # because some logic happens only if we aren't using the default.
141 config_file_specified = Set()
141 config_file_specified = Set()
142
142
143 config_file_name = Unicode()
143 config_file_name = Unicode()
144 @default('config_file_name')
144 @default('config_file_name')
145 def _config_file_name_default(self):
145 def _config_file_name_default(self):
146 return self.name.replace('-','_') + u'_config.py'
146 return self.name.replace('-','_') + u'_config.py'
147 @observe('config_file_name')
147 @observe('config_file_name')
148 def _config_file_name_changed(self, change):
148 def _config_file_name_changed(self, change):
149 if change['new'] != change['old']:
149 if change['new'] != change['old']:
150 self.config_file_specified.add(change['new'])
150 self.config_file_specified.add(change['new'])
151
151
152 # The directory that contains IPython's builtin profiles.
152 # The directory that contains IPython's builtin profiles.
153 builtin_profile_dir = Unicode(
153 builtin_profile_dir = Unicode(
154 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
154 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
155 )
155 )
156
156
157 config_file_paths = List(Unicode())
157 config_file_paths = List(Unicode())
158 @default('config_file_paths')
158 @default('config_file_paths')
159 def _config_file_paths_default(self):
159 def _config_file_paths_default(self):
160 return [os.getcwd()]
160 return [os.getcwd()]
161
161
162 extra_config_file = Unicode(
162 extra_config_file = Unicode(
163 help="""Path to an extra config file to load.
163 help="""Path to an extra config file to load.
164
164
165 If specified, load this config file in addition to any other IPython config.
165 If specified, load this config file in addition to any other IPython config.
166 """).tag(config=True)
166 """).tag(config=True)
167 @observe('extra_config_file')
167 @observe('extra_config_file')
168 def _extra_config_file_changed(self, change):
168 def _extra_config_file_changed(self, change):
169 old = change['old']
169 old = change['old']
170 new = change['new']
170 new = change['new']
171 try:
171 try:
172 self.config_files.remove(old)
172 self.config_files.remove(old)
173 except ValueError:
173 except ValueError:
174 pass
174 pass
175 self.config_file_specified.add(new)
175 self.config_file_specified.add(new)
176 self.config_files.append(new)
176 self.config_files.append(new)
177
177
178 profile = Unicode(u'default',
178 profile = Unicode(u'default',
179 help="""The IPython profile to use."""
179 help="""The IPython profile to use."""
180 ).tag(config=True)
180 ).tag(config=True)
181
181
182 @observe('profile')
182 @observe('profile')
183 def _profile_changed(self, change):
183 def _profile_changed(self, change):
184 self.builtin_profile_dir = os.path.join(
184 self.builtin_profile_dir = os.path.join(
185 get_ipython_package_dir(), u'config', u'profile', change['new']
185 get_ipython_package_dir(), u'config', u'profile', change['new']
186 )
186 )
187
187
188 ipython_dir = Unicode(
188 ipython_dir = Unicode(
189 help="""
189 help="""
190 The name of the IPython directory. This directory is used for logging
190 The name of the IPython directory. This directory is used for logging
191 configuration (through profiles), history storage, etc. The default
191 configuration (through profiles), history storage, etc. The default
192 is usually $HOME/.ipython. This option can also be specified through
192 is usually $HOME/.ipython. This option can also be specified through
193 the environment variable IPYTHONDIR.
193 the environment variable IPYTHONDIR.
194 """
194 """
195 ).tag(config=True)
195 ).tag(config=True)
196 @default('ipython_dir')
196 @default('ipython_dir')
197 def _ipython_dir_default(self):
197 def _ipython_dir_default(self):
198 d = get_ipython_dir()
198 d = get_ipython_dir()
199 self._ipython_dir_changed({
199 self._ipython_dir_changed({
200 'name': 'ipython_dir',
200 'name': 'ipython_dir',
201 'old': d,
201 'old': d,
202 'new': d,
202 'new': d,
203 })
203 })
204 return d
204 return d
205
205
206 _in_init_profile_dir = False
206 _in_init_profile_dir = False
207 profile_dir = Instance(ProfileDir, allow_none=True)
207 profile_dir = Instance(ProfileDir, allow_none=True)
208 @default('profile_dir')
208 @default('profile_dir')
209 def _profile_dir_default(self):
209 def _profile_dir_default(self):
210 # avoid recursion
210 # avoid recursion
211 if self._in_init_profile_dir:
211 if self._in_init_profile_dir:
212 return
212 return
213 # profile_dir requested early, force initialization
213 # profile_dir requested early, force initialization
214 self.init_profile_dir()
214 self.init_profile_dir()
215 return self.profile_dir
215 return self.profile_dir
216
216
217 overwrite = Bool(False,
217 overwrite = Bool(False,
218 help="""Whether to overwrite existing config files when copying"""
218 help="""Whether to overwrite existing config files when copying"""
219 ).tag(config=True)
219 ).tag(config=True)
220 auto_create = Bool(False,
220 auto_create = Bool(False,
221 help="""Whether to create profile dir if it doesn't exist"""
221 help="""Whether to create profile dir if it doesn't exist"""
222 ).tag(config=True)
222 ).tag(config=True)
223
223
224 config_files = List(Unicode())
224 config_files = List(Unicode())
225 @default('config_files')
225 @default('config_files')
226 def _config_files_default(self):
226 def _config_files_default(self):
227 return [self.config_file_name]
227 return [self.config_file_name]
228
228
229 copy_config_files = Bool(False,
229 copy_config_files = Bool(False,
230 help="""Whether to install the default config files into the profile dir.
230 help="""Whether to install the default config files into the profile dir.
231 If a new profile is being created, and IPython contains config files for that
231 If a new profile is being created, and IPython contains config files for that
232 profile, then they will be staged into the new directory. Otherwise,
232 profile, then they will be staged into the new directory. Otherwise,
233 default config files will be automatically generated.
233 default config files will be automatically generated.
234 """).tag(config=True)
234 """).tag(config=True)
235
235
236 verbose_crash = Bool(False,
236 verbose_crash = Bool(False,
237 help="""Create a massive crash report when IPython encounters what may be an
237 help="""Create a massive crash report when IPython encounters what may be an
238 internal error. The default is to append a short message to the
238 internal error. The default is to append a short message to the
239 usual traceback""").tag(config=True)
239 usual traceback""").tag(config=True)
240
240
241 # The class to use as the crash handler.
241 # The class to use as the crash handler.
242 crash_handler_class = Type(crashhandler.CrashHandler)
242 crash_handler_class = Type(crashhandler.CrashHandler)
243
243
244 @catch_config_error
244 @catch_config_error
245 def __init__(self, **kwargs):
245 def __init__(self, **kwargs):
246 super(BaseIPythonApplication, self).__init__(**kwargs)
246 super(BaseIPythonApplication, self).__init__(**kwargs)
247 # ensure current working directory exists
247 # ensure current working directory exists
248 try:
248 try:
249 os.getcwd()
249 os.getcwd()
250 except:
250 except:
251 # exit if cwd doesn't exist
251 # exit if cwd doesn't exist
252 self.log.error("Current working directory doesn't exist.")
252 self.log.error("Current working directory doesn't exist.")
253 self.exit(1)
253 self.exit(1)
254
254
255 #-------------------------------------------------------------------------
255 #-------------------------------------------------------------------------
256 # Various stages of Application creation
256 # Various stages of Application creation
257 #-------------------------------------------------------------------------
257 #-------------------------------------------------------------------------
258
258
259 deprecated_subcommands = {}
259 deprecated_subcommands = {}
260
260
261 def initialize_subcommand(self, subc, argv=None):
261 def initialize_subcommand(self, subc, argv=None):
262 if subc in self.deprecated_subcommands:
262 if subc in self.deprecated_subcommands:
263 self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed "
263 self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed "
264 "in future versions.".format(sub=subc))
264 "in future versions.".format(sub=subc))
265 self.log.warning("You likely want to use `jupyter {sub}` in the "
265 self.log.warning("You likely want to use `jupyter {sub}` in the "
266 "future".format(sub=subc))
266 "future".format(sub=subc))
267 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
267 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
268
268
269 def init_crash_handler(self):
269 def init_crash_handler(self):
270 """Create a crash handler, typically setting sys.excepthook to it."""
270 """Create a crash handler, typically setting sys.excepthook to it."""
271 self.crash_handler = self.crash_handler_class(self)
271 self.crash_handler = self.crash_handler_class(self)
272 sys.excepthook = self.excepthook
272 sys.excepthook = self.excepthook
273 def unset_crashhandler():
273 def unset_crashhandler():
274 sys.excepthook = sys.__excepthook__
274 sys.excepthook = sys.__excepthook__
275 atexit.register(unset_crashhandler)
275 atexit.register(unset_crashhandler)
276
276
277 def excepthook(self, etype, evalue, tb):
277 def excepthook(self, etype, evalue, tb):
278 """this is sys.excepthook after init_crashhandler
278 """this is sys.excepthook after init_crashhandler
279
279
280 set self.verbose_crash=True to use our full crashhandler, instead of
280 set self.verbose_crash=True to use our full crashhandler, instead of
281 a regular traceback with a short message (crash_handler_lite)
281 a regular traceback with a short message (crash_handler_lite)
282 """
282 """
283
283
284 if self.verbose_crash:
284 if self.verbose_crash:
285 return self.crash_handler(etype, evalue, tb)
285 return self.crash_handler(etype, evalue, tb)
286 else:
286 else:
287 return crashhandler.crash_handler_lite(etype, evalue, tb)
287 return crashhandler.crash_handler_lite(etype, evalue, tb)
288
288
289 @observe('ipython_dir')
289 @observe('ipython_dir')
290 def _ipython_dir_changed(self, change):
290 def _ipython_dir_changed(self, change):
291 old = change['old']
291 old = change['old']
292 new = change['new']
292 new = change['new']
293 if old is not Undefined:
293 if old is not Undefined:
294 str_old = os.path.abspath(old)
294 str_old = os.path.abspath(old)
295 if str_old in sys.path:
295 if str_old in sys.path:
296 sys.path.remove(str_old)
296 sys.path.remove(str_old)
297 str_path = os.path.abspath(new)
297 str_path = os.path.abspath(new)
298 sys.path.append(str_path)
298 sys.path.append(str_path)
299 ensure_dir_exists(new)
299 ensure_dir_exists(new)
300 readme = os.path.join(new, 'README')
300 readme = os.path.join(new, 'README')
301 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
301 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
302 if not os.path.exists(readme) and os.path.exists(readme_src):
302 if not os.path.exists(readme) and os.path.exists(readme_src):
303 shutil.copy(readme_src, readme)
303 shutil.copy(readme_src, readme)
304 for d in ('extensions', 'nbextensions'):
304 for d in ('extensions', 'nbextensions'):
305 path = os.path.join(new, d)
305 path = os.path.join(new, d)
306 try:
306 try:
307 ensure_dir_exists(path)
307 ensure_dir_exists(path)
308 except OSError as e:
308 except OSError as e:
309 # this will not be EEXIST
309 # this will not be EEXIST
310 self.log.error("couldn't create path %s: %s", path, e)
310 self.log.error("couldn't create path %s: %s", path, e)
311 self.log.debug("IPYTHONDIR set to: %s" % new)
311 self.log.debug("IPYTHONDIR set to: %s" % new)
312
312
313 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
313 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
314 """Load the config file.
314 """Load the config file.
315
315
316 By default, errors in loading config are handled, and a warning
316 By default, errors in loading config are handled, and a warning
317 printed on screen. For testing, the suppress_errors option is set
317 printed on screen. For testing, the suppress_errors option is set
318 to False, so errors will make tests fail.
318 to False, so errors will make tests fail.
319
319
320 `suppress_errors` default value is to be `None` in which case the
320 `suppress_errors` default value is to be `None` in which case the
321 behavior default to the one of `traitlets.Application`.
321 behavior default to the one of `traitlets.Application`.
322
322
323 The default value can be set :
323 The default value can be set :
324 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
324 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
325 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
325 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
326 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
326 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
327
327
328 Any other value are invalid, and will make IPython exit with a non-zero return code.
328 Any other value are invalid, and will make IPython exit with a non-zero return code.
329 """
329 """
330
330
331
331
332 self.log.debug("Searching path %s for config files", self.config_file_paths)
332 self.log.debug("Searching path %s for config files", self.config_file_paths)
333 base_config = 'ipython_config.py'
333 base_config = 'ipython_config.py'
334 self.log.debug("Attempting to load config file: %s" %
334 self.log.debug("Attempting to load config file: %s" %
335 base_config)
335 base_config)
336 try:
336 try:
337 if suppress_errors is not None:
337 if suppress_errors is not None:
338 old_value = Application.raise_config_file_errors
338 old_value = Application.raise_config_file_errors
339 Application.raise_config_file_errors = not suppress_errors;
339 Application.raise_config_file_errors = not suppress_errors;
340 Application.load_config_file(
340 Application.load_config_file(
341 self,
341 self,
342 base_config,
342 base_config,
343 path=self.config_file_paths
343 path=self.config_file_paths
344 )
344 )
345 except ConfigFileNotFound:
345 except ConfigFileNotFound:
346 # ignore errors loading parent
346 # ignore errors loading parent
347 self.log.debug("Config file %s not found", base_config)
347 self.log.debug("Config file %s not found", base_config)
348 pass
348 pass
349 if suppress_errors is not None:
349 if suppress_errors is not None:
350 Application.raise_config_file_errors = old_value
350 Application.raise_config_file_errors = old_value
351
351
352 for config_file_name in self.config_files:
352 for config_file_name in self.config_files:
353 if not config_file_name or config_file_name == base_config:
353 if not config_file_name or config_file_name == base_config:
354 continue
354 continue
355 self.log.debug("Attempting to load config file: %s" %
355 self.log.debug("Attempting to load config file: %s" %
356 self.config_file_name)
356 self.config_file_name)
357 try:
357 try:
358 Application.load_config_file(
358 Application.load_config_file(
359 self,
359 self,
360 config_file_name,
360 config_file_name,
361 path=self.config_file_paths
361 path=self.config_file_paths
362 )
362 )
363 except ConfigFileNotFound:
363 except ConfigFileNotFound:
364 # Only warn if the default config file was NOT being used.
364 # Only warn if the default config file was NOT being used.
365 if config_file_name in self.config_file_specified:
365 if config_file_name in self.config_file_specified:
366 msg = self.log.warning
366 msg = self.log.warning
367 else:
367 else:
368 msg = self.log.debug
368 msg = self.log.debug
369 msg("Config file not found, skipping: %s", config_file_name)
369 msg("Config file not found, skipping: %s", config_file_name)
370 except Exception:
370 except Exception:
371 # For testing purposes.
371 # For testing purposes.
372 if not suppress_errors:
372 if not suppress_errors:
373 raise
373 raise
374 self.log.warning("Error loading config file: %s" %
374 self.log.warning("Error loading config file: %s" %
375 self.config_file_name, exc_info=True)
375 self.config_file_name, exc_info=True)
376
376
377 def init_profile_dir(self):
377 def init_profile_dir(self):
378 """initialize the profile dir"""
378 """initialize the profile dir"""
379 self._in_init_profile_dir = True
379 self._in_init_profile_dir = True
380 if self.profile_dir is not None:
380 if self.profile_dir is not None:
381 # already ran
381 # already ran
382 return
382 return
383 if 'ProfileDir.location' not in self.config:
383 if 'ProfileDir.location' not in self.config:
384 # location not specified, find by profile name
384 # location not specified, find by profile name
385 try:
385 try:
386 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
386 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
387 except ProfileDirError:
387 except ProfileDirError:
388 # not found, maybe create it (always create default profile)
388 # not found, maybe create it (always create default profile)
389 if self.auto_create or self.profile == 'default':
389 if self.auto_create or self.profile == 'default':
390 try:
390 try:
391 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
391 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
392 except ProfileDirError:
392 except ProfileDirError:
393 self.log.fatal("Could not create profile: %r"%self.profile)
393 self.log.fatal("Could not create profile: %r"%self.profile)
394 self.exit(1)
394 self.exit(1)
395 else:
395 else:
396 self.log.info("Created profile dir: %r"%p.location)
396 self.log.info("Created profile dir: %r"%p.location)
397 else:
397 else:
398 self.log.fatal("Profile %r not found."%self.profile)
398 self.log.fatal("Profile %r not found."%self.profile)
399 self.exit(1)
399 self.exit(1)
400 else:
400 else:
401 self.log.debug("Using existing profile dir: %r"%p.location)
401 self.log.debug(f"Using existing profile dir: {p.location!r}")
402 else:
402 else:
403 location = self.config.ProfileDir.location
403 location = self.config.ProfileDir.location
404 # location is fully specified
404 # location is fully specified
405 try:
405 try:
406 p = ProfileDir.find_profile_dir(location, self.config)
406 p = ProfileDir.find_profile_dir(location, self.config)
407 except ProfileDirError:
407 except ProfileDirError:
408 # not found, maybe create it
408 # not found, maybe create it
409 if self.auto_create:
409 if self.auto_create:
410 try:
410 try:
411 p = ProfileDir.create_profile_dir(location, self.config)
411 p = ProfileDir.create_profile_dir(location, self.config)
412 except ProfileDirError:
412 except ProfileDirError:
413 self.log.fatal("Could not create profile directory: %r"%location)
413 self.log.fatal("Could not create profile directory: %r"%location)
414 self.exit(1)
414 self.exit(1)
415 else:
415 else:
416 self.log.debug("Creating new profile dir: %r"%location)
416 self.log.debug("Creating new profile dir: %r"%location)
417 else:
417 else:
418 self.log.fatal("Profile directory %r not found."%location)
418 self.log.fatal("Profile directory %r not found."%location)
419 self.exit(1)
419 self.exit(1)
420 else:
420 else:
421 self.log.info("Using existing profile dir: %r"%location)
421 self.log.debug(f"Using existing profile dir: {p.location!r}")
422 # if profile_dir is specified explicitly, set profile name
422 # if profile_dir is specified explicitly, set profile name
423 dir_name = os.path.basename(p.location)
423 dir_name = os.path.basename(p.location)
424 if dir_name.startswith('profile_'):
424 if dir_name.startswith('profile_'):
425 self.profile = dir_name[8:]
425 self.profile = dir_name[8:]
426
426
427 self.profile_dir = p
427 self.profile_dir = p
428 self.config_file_paths.append(p.location)
428 self.config_file_paths.append(p.location)
429 self._in_init_profile_dir = False
429 self._in_init_profile_dir = False
430
430
431 def init_config_files(self):
431 def init_config_files(self):
432 """[optionally] copy default config files into profile dir."""
432 """[optionally] copy default config files into profile dir."""
433 self.config_file_paths.extend(ENV_CONFIG_DIRS)
433 self.config_file_paths.extend(ENV_CONFIG_DIRS)
434 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
434 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
435 # copy config files
435 # copy config files
436 path = Path(self.builtin_profile_dir)
436 path = Path(self.builtin_profile_dir)
437 if self.copy_config_files:
437 if self.copy_config_files:
438 src = self.profile
438 src = self.profile
439
439
440 cfg = self.config_file_name
440 cfg = self.config_file_name
441 if path and (path / cfg).exists():
441 if path and (path / cfg).exists():
442 self.log.warning(
442 self.log.warning(
443 "Staging %r from %s into %r [overwrite=%s]"
443 "Staging %r from %s into %r [overwrite=%s]"
444 % (cfg, src, self.profile_dir.location, self.overwrite)
444 % (cfg, src, self.profile_dir.location, self.overwrite)
445 )
445 )
446 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
446 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
447 else:
447 else:
448 self.stage_default_config_file()
448 self.stage_default_config_file()
449 else:
449 else:
450 # Still stage *bundled* config files, but not generated ones
450 # Still stage *bundled* config files, but not generated ones
451 # This is necessary for `ipython profile=sympy` to load the profile
451 # This is necessary for `ipython profile=sympy` to load the profile
452 # on the first go
452 # on the first go
453 files = path.glob("*.py")
453 files = path.glob("*.py")
454 for fullpath in files:
454 for fullpath in files:
455 cfg = fullpath.name
455 cfg = fullpath.name
456 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
456 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
457 # file was copied
457 # file was copied
458 self.log.warning("Staging bundled %s from %s into %r"%(
458 self.log.warning("Staging bundled %s from %s into %r"%(
459 cfg, self.profile, self.profile_dir.location)
459 cfg, self.profile, self.profile_dir.location)
460 )
460 )
461
461
462
462
463 def stage_default_config_file(self):
463 def stage_default_config_file(self):
464 """auto generate default config file, and stage it into the profile."""
464 """auto generate default config file, and stage it into the profile."""
465 s = self.generate_config_file()
465 s = self.generate_config_file()
466 config_file = Path(self.profile_dir.location) / self.config_file_name
466 config_file = Path(self.profile_dir.location) / self.config_file_name
467 if self.overwrite or not config_file.exists():
467 if self.overwrite or not config_file.exists():
468 self.log.warning("Generating default config file: %r" % (config_file))
468 self.log.warning("Generating default config file: %r" % (config_file))
469 config_file.write_text(s)
469 config_file.write_text(s)
470
470
471 @catch_config_error
471 @catch_config_error
472 def initialize(self, argv=None):
472 def initialize(self, argv=None):
473 # don't hook up crash handler before parsing command-line
473 # don't hook up crash handler before parsing command-line
474 self.parse_command_line(argv)
474 self.parse_command_line(argv)
475 self.init_crash_handler()
475 self.init_crash_handler()
476 if self.subapp is not None:
476 if self.subapp is not None:
477 # stop here if subapp is taking over
477 # stop here if subapp is taking over
478 return
478 return
479 # save a copy of CLI config to re-load after config files
479 # save a copy of CLI config to re-load after config files
480 # so that it has highest priority
480 # so that it has highest priority
481 cl_config = deepcopy(self.config)
481 cl_config = deepcopy(self.config)
482 self.init_profile_dir()
482 self.init_profile_dir()
483 self.init_config_files()
483 self.init_config_files()
484 self.load_config_file()
484 self.load_config_file()
485 # enforce cl-opts override configfile opts:
485 # enforce cl-opts override configfile opts:
486 self.update_config(cl_config)
486 self.update_config(cl_config)
General Comments 0
You need to be logged in to leave comments. Login now