##// END OF EJS Templates
Fix case where programdata is None on windows....
Matthias Bussonnier -
Show More
@@ -1,476 +1,476 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 = os.environ.get("PROGRAMDATA", None)
38 if programdata:
38 if programdata is not None:
39 SYSTEM_CONFIG_DIRS = [str(programdata / "ipython")]
39 SYSTEM_CONFIG_DIRS = [str(Path(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 def init_crash_handler(self):
259 def init_crash_handler(self):
260 """Create a crash handler, typically setting sys.excepthook to it."""
260 """Create a crash handler, typically setting sys.excepthook to it."""
261 self.crash_handler = self.crash_handler_class(self)
261 self.crash_handler = self.crash_handler_class(self)
262 sys.excepthook = self.excepthook
262 sys.excepthook = self.excepthook
263 def unset_crashhandler():
263 def unset_crashhandler():
264 sys.excepthook = sys.__excepthook__
264 sys.excepthook = sys.__excepthook__
265 atexit.register(unset_crashhandler)
265 atexit.register(unset_crashhandler)
266
266
267 def excepthook(self, etype, evalue, tb):
267 def excepthook(self, etype, evalue, tb):
268 """this is sys.excepthook after init_crashhandler
268 """this is sys.excepthook after init_crashhandler
269
269
270 set self.verbose_crash=True to use our full crashhandler, instead of
270 set self.verbose_crash=True to use our full crashhandler, instead of
271 a regular traceback with a short message (crash_handler_lite)
271 a regular traceback with a short message (crash_handler_lite)
272 """
272 """
273
273
274 if self.verbose_crash:
274 if self.verbose_crash:
275 return self.crash_handler(etype, evalue, tb)
275 return self.crash_handler(etype, evalue, tb)
276 else:
276 else:
277 return crashhandler.crash_handler_lite(etype, evalue, tb)
277 return crashhandler.crash_handler_lite(etype, evalue, tb)
278
278
279 @observe('ipython_dir')
279 @observe('ipython_dir')
280 def _ipython_dir_changed(self, change):
280 def _ipython_dir_changed(self, change):
281 old = change['old']
281 old = change['old']
282 new = change['new']
282 new = change['new']
283 if old is not Undefined:
283 if old is not Undefined:
284 str_old = os.path.abspath(old)
284 str_old = os.path.abspath(old)
285 if str_old in sys.path:
285 if str_old in sys.path:
286 sys.path.remove(str_old)
286 sys.path.remove(str_old)
287 str_path = os.path.abspath(new)
287 str_path = os.path.abspath(new)
288 sys.path.append(str_path)
288 sys.path.append(str_path)
289 ensure_dir_exists(new)
289 ensure_dir_exists(new)
290 readme = os.path.join(new, 'README')
290 readme = os.path.join(new, 'README')
291 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
291 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
292 if not os.path.exists(readme) and os.path.exists(readme_src):
292 if not os.path.exists(readme) and os.path.exists(readme_src):
293 shutil.copy(readme_src, readme)
293 shutil.copy(readme_src, readme)
294 for d in ('extensions', 'nbextensions'):
294 for d in ('extensions', 'nbextensions'):
295 path = os.path.join(new, d)
295 path = os.path.join(new, d)
296 try:
296 try:
297 ensure_dir_exists(path)
297 ensure_dir_exists(path)
298 except OSError as e:
298 except OSError as e:
299 # this will not be EEXIST
299 # this will not be EEXIST
300 self.log.error("couldn't create path %s: %s", path, e)
300 self.log.error("couldn't create path %s: %s", path, e)
301 self.log.debug("IPYTHONDIR set to: %s" % new)
301 self.log.debug("IPYTHONDIR set to: %s" % new)
302
302
303 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
303 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
304 """Load the config file.
304 """Load the config file.
305
305
306 By default, errors in loading config are handled, and a warning
306 By default, errors in loading config are handled, and a warning
307 printed on screen. For testing, the suppress_errors option is set
307 printed on screen. For testing, the suppress_errors option is set
308 to False, so errors will make tests fail.
308 to False, so errors will make tests fail.
309
309
310 `suppress_errors` default value is to be `None` in which case the
310 `suppress_errors` default value is to be `None` in which case the
311 behavior default to the one of `traitlets.Application`.
311 behavior default to the one of `traitlets.Application`.
312
312
313 The default value can be set :
313 The default value can be set :
314 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
314 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
315 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
315 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
316 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
316 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
317
317
318 Any other value are invalid, and will make IPython exit with a non-zero return code.
318 Any other value are invalid, and will make IPython exit with a non-zero return code.
319 """
319 """
320
320
321
321
322 self.log.debug("Searching path %s for config files", self.config_file_paths)
322 self.log.debug("Searching path %s for config files", self.config_file_paths)
323 base_config = 'ipython_config.py'
323 base_config = 'ipython_config.py'
324 self.log.debug("Attempting to load config file: %s" %
324 self.log.debug("Attempting to load config file: %s" %
325 base_config)
325 base_config)
326 try:
326 try:
327 if suppress_errors is not None:
327 if suppress_errors is not None:
328 old_value = Application.raise_config_file_errors
328 old_value = Application.raise_config_file_errors
329 Application.raise_config_file_errors = not suppress_errors;
329 Application.raise_config_file_errors = not suppress_errors;
330 Application.load_config_file(
330 Application.load_config_file(
331 self,
331 self,
332 base_config,
332 base_config,
333 path=self.config_file_paths
333 path=self.config_file_paths
334 )
334 )
335 except ConfigFileNotFound:
335 except ConfigFileNotFound:
336 # ignore errors loading parent
336 # ignore errors loading parent
337 self.log.debug("Config file %s not found", base_config)
337 self.log.debug("Config file %s not found", base_config)
338 pass
338 pass
339 if suppress_errors is not None:
339 if suppress_errors is not None:
340 Application.raise_config_file_errors = old_value
340 Application.raise_config_file_errors = old_value
341
341
342 for config_file_name in self.config_files:
342 for config_file_name in self.config_files:
343 if not config_file_name or config_file_name == base_config:
343 if not config_file_name or config_file_name == base_config:
344 continue
344 continue
345 self.log.debug("Attempting to load config file: %s" %
345 self.log.debug("Attempting to load config file: %s" %
346 self.config_file_name)
346 self.config_file_name)
347 try:
347 try:
348 Application.load_config_file(
348 Application.load_config_file(
349 self,
349 self,
350 config_file_name,
350 config_file_name,
351 path=self.config_file_paths
351 path=self.config_file_paths
352 )
352 )
353 except ConfigFileNotFound:
353 except ConfigFileNotFound:
354 # Only warn if the default config file was NOT being used.
354 # Only warn if the default config file was NOT being used.
355 if config_file_name in self.config_file_specified:
355 if config_file_name in self.config_file_specified:
356 msg = self.log.warning
356 msg = self.log.warning
357 else:
357 else:
358 msg = self.log.debug
358 msg = self.log.debug
359 msg("Config file not found, skipping: %s", config_file_name)
359 msg("Config file not found, skipping: %s", config_file_name)
360 except Exception:
360 except Exception:
361 # For testing purposes.
361 # For testing purposes.
362 if not suppress_errors:
362 if not suppress_errors:
363 raise
363 raise
364 self.log.warning("Error loading config file: %s" %
364 self.log.warning("Error loading config file: %s" %
365 self.config_file_name, exc_info=True)
365 self.config_file_name, exc_info=True)
366
366
367 def init_profile_dir(self):
367 def init_profile_dir(self):
368 """initialize the profile dir"""
368 """initialize the profile dir"""
369 self._in_init_profile_dir = True
369 self._in_init_profile_dir = True
370 if self.profile_dir is not None:
370 if self.profile_dir is not None:
371 # already ran
371 # already ran
372 return
372 return
373 if 'ProfileDir.location' not in self.config:
373 if 'ProfileDir.location' not in self.config:
374 # location not specified, find by profile name
374 # location not specified, find by profile name
375 try:
375 try:
376 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
376 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
377 except ProfileDirError:
377 except ProfileDirError:
378 # not found, maybe create it (always create default profile)
378 # not found, maybe create it (always create default profile)
379 if self.auto_create or self.profile == 'default':
379 if self.auto_create or self.profile == 'default':
380 try:
380 try:
381 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
381 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
382 except ProfileDirError:
382 except ProfileDirError:
383 self.log.fatal("Could not create profile: %r"%self.profile)
383 self.log.fatal("Could not create profile: %r"%self.profile)
384 self.exit(1)
384 self.exit(1)
385 else:
385 else:
386 self.log.info("Created profile dir: %r"%p.location)
386 self.log.info("Created profile dir: %r"%p.location)
387 else:
387 else:
388 self.log.fatal("Profile %r not found."%self.profile)
388 self.log.fatal("Profile %r not found."%self.profile)
389 self.exit(1)
389 self.exit(1)
390 else:
390 else:
391 self.log.debug(f"Using existing profile dir: {p.location!r}")
391 self.log.debug(f"Using existing profile dir: {p.location!r}")
392 else:
392 else:
393 location = self.config.ProfileDir.location
393 location = self.config.ProfileDir.location
394 # location is fully specified
394 # location is fully specified
395 try:
395 try:
396 p = ProfileDir.find_profile_dir(location, self.config)
396 p = ProfileDir.find_profile_dir(location, self.config)
397 except ProfileDirError:
397 except ProfileDirError:
398 # not found, maybe create it
398 # not found, maybe create it
399 if self.auto_create:
399 if self.auto_create:
400 try:
400 try:
401 p = ProfileDir.create_profile_dir(location, self.config)
401 p = ProfileDir.create_profile_dir(location, self.config)
402 except ProfileDirError:
402 except ProfileDirError:
403 self.log.fatal("Could not create profile directory: %r"%location)
403 self.log.fatal("Could not create profile directory: %r"%location)
404 self.exit(1)
404 self.exit(1)
405 else:
405 else:
406 self.log.debug("Creating new profile dir: %r"%location)
406 self.log.debug("Creating new profile dir: %r"%location)
407 else:
407 else:
408 self.log.fatal("Profile directory %r not found."%location)
408 self.log.fatal("Profile directory %r not found."%location)
409 self.exit(1)
409 self.exit(1)
410 else:
410 else:
411 self.log.debug(f"Using existing profile dir: {p.location!r}")
411 self.log.debug(f"Using existing profile dir: {p.location!r}")
412 # if profile_dir is specified explicitly, set profile name
412 # if profile_dir is specified explicitly, set profile name
413 dir_name = os.path.basename(p.location)
413 dir_name = os.path.basename(p.location)
414 if dir_name.startswith('profile_'):
414 if dir_name.startswith('profile_'):
415 self.profile = dir_name[8:]
415 self.profile = dir_name[8:]
416
416
417 self.profile_dir = p
417 self.profile_dir = p
418 self.config_file_paths.append(p.location)
418 self.config_file_paths.append(p.location)
419 self._in_init_profile_dir = False
419 self._in_init_profile_dir = False
420
420
421 def init_config_files(self):
421 def init_config_files(self):
422 """[optionally] copy default config files into profile dir."""
422 """[optionally] copy default config files into profile dir."""
423 self.config_file_paths.extend(ENV_CONFIG_DIRS)
423 self.config_file_paths.extend(ENV_CONFIG_DIRS)
424 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
424 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
425 # copy config files
425 # copy config files
426 path = Path(self.builtin_profile_dir)
426 path = Path(self.builtin_profile_dir)
427 if self.copy_config_files:
427 if self.copy_config_files:
428 src = self.profile
428 src = self.profile
429
429
430 cfg = self.config_file_name
430 cfg = self.config_file_name
431 if path and (path / cfg).exists():
431 if path and (path / cfg).exists():
432 self.log.warning(
432 self.log.warning(
433 "Staging %r from %s into %r [overwrite=%s]"
433 "Staging %r from %s into %r [overwrite=%s]"
434 % (cfg, src, self.profile_dir.location, self.overwrite)
434 % (cfg, src, self.profile_dir.location, self.overwrite)
435 )
435 )
436 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
436 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
437 else:
437 else:
438 self.stage_default_config_file()
438 self.stage_default_config_file()
439 else:
439 else:
440 # Still stage *bundled* config files, but not generated ones
440 # Still stage *bundled* config files, but not generated ones
441 # This is necessary for `ipython profile=sympy` to load the profile
441 # This is necessary for `ipython profile=sympy` to load the profile
442 # on the first go
442 # on the first go
443 files = path.glob("*.py")
443 files = path.glob("*.py")
444 for fullpath in files:
444 for fullpath in files:
445 cfg = fullpath.name
445 cfg = fullpath.name
446 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
446 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
447 # file was copied
447 # file was copied
448 self.log.warning("Staging bundled %s from %s into %r"%(
448 self.log.warning("Staging bundled %s from %s into %r"%(
449 cfg, self.profile, self.profile_dir.location)
449 cfg, self.profile, self.profile_dir.location)
450 )
450 )
451
451
452
452
453 def stage_default_config_file(self):
453 def stage_default_config_file(self):
454 """auto generate default config file, and stage it into the profile."""
454 """auto generate default config file, and stage it into the profile."""
455 s = self.generate_config_file()
455 s = self.generate_config_file()
456 config_file = Path(self.profile_dir.location) / self.config_file_name
456 config_file = Path(self.profile_dir.location) / self.config_file_name
457 if self.overwrite or not config_file.exists():
457 if self.overwrite or not config_file.exists():
458 self.log.warning("Generating default config file: %r" % (config_file))
458 self.log.warning("Generating default config file: %r" % (config_file))
459 config_file.write_text(s)
459 config_file.write_text(s)
460
460
461 @catch_config_error
461 @catch_config_error
462 def initialize(self, argv=None):
462 def initialize(self, argv=None):
463 # don't hook up crash handler before parsing command-line
463 # don't hook up crash handler before parsing command-line
464 self.parse_command_line(argv)
464 self.parse_command_line(argv)
465 self.init_crash_handler()
465 self.init_crash_handler()
466 if self.subapp is not None:
466 if self.subapp is not None:
467 # stop here if subapp is taking over
467 # stop here if subapp is taking over
468 return
468 return
469 # save a copy of CLI config to re-load after config files
469 # save a copy of CLI config to re-load after config files
470 # so that it has highest priority
470 # so that it has highest priority
471 cl_config = deepcopy(self.config)
471 cl_config = deepcopy(self.config)
472 self.init_profile_dir()
472 self.init_profile_dir()
473 self.init_config_files()
473 self.init_config_files()
474 self.load_config_file()
474 self.load_config_file()
475 # enforce cl-opts override configfile opts:
475 # enforce cl-opts override configfile opts:
476 self.update_config(cl_config)
476 self.update_config(cl_config)
General Comments 0
You need to be logged in to leave comments. Login now