##// END OF EJS Templates
Merge pull request #13345 from Carreau/no-sys-path-append...
Matthias Bussonnier -
r27377:a2ae81e8 merge
parent child Browse files
Show More
@@ -1,476 +1,490 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 = os.environ.get("PROGRAMDATA", None)
37 programdata = os.environ.get("PROGRAMDATA", None)
38 if programdata is not None:
38 if programdata is not None:
39 SYSTEM_CONFIG_DIRS = [str(Path(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 add_ipython_dir_to_sys_path = Bool(
189 False,
190 """Should the IPython profile directory be added to sys path ?
191
192 This option was non-existing before IPython 8.0, and ipython_dir was added to
193 sys path to allow import of extensions present there. This was historical
194 baggage from when pip did not exist. This now default to false,
195 but can be set to true for legacy reasons.
196 """,
197 ).tag(config=True)
198
188 ipython_dir = Unicode(
199 ipython_dir = Unicode(
189 help="""
200 help="""
190 The name of the IPython directory. This directory is used for logging
201 The name of the IPython directory. This directory is used for logging
191 configuration (through profiles), history storage, etc. The default
202 configuration (through profiles), history storage, etc. The default
192 is usually $HOME/.ipython. This option can also be specified through
203 is usually $HOME/.ipython. This option can also be specified through
193 the environment variable IPYTHONDIR.
204 the environment variable IPYTHONDIR.
194 """
205 """
195 ).tag(config=True)
206 ).tag(config=True)
196 @default('ipython_dir')
207 @default('ipython_dir')
197 def _ipython_dir_default(self):
208 def _ipython_dir_default(self):
198 d = get_ipython_dir()
209 d = get_ipython_dir()
199 self._ipython_dir_changed({
210 self._ipython_dir_changed({
200 'name': 'ipython_dir',
211 'name': 'ipython_dir',
201 'old': d,
212 'old': d,
202 'new': d,
213 'new': d,
203 })
214 })
204 return d
215 return d
205
216
206 _in_init_profile_dir = False
217 _in_init_profile_dir = False
207 profile_dir = Instance(ProfileDir, allow_none=True)
218 profile_dir = Instance(ProfileDir, allow_none=True)
208 @default('profile_dir')
219 @default('profile_dir')
209 def _profile_dir_default(self):
220 def _profile_dir_default(self):
210 # avoid recursion
221 # avoid recursion
211 if self._in_init_profile_dir:
222 if self._in_init_profile_dir:
212 return
223 return
213 # profile_dir requested early, force initialization
224 # profile_dir requested early, force initialization
214 self.init_profile_dir()
225 self.init_profile_dir()
215 return self.profile_dir
226 return self.profile_dir
216
227
217 overwrite = Bool(False,
228 overwrite = Bool(False,
218 help="""Whether to overwrite existing config files when copying"""
229 help="""Whether to overwrite existing config files when copying"""
219 ).tag(config=True)
230 ).tag(config=True)
220 auto_create = Bool(False,
231 auto_create = Bool(False,
221 help="""Whether to create profile dir if it doesn't exist"""
232 help="""Whether to create profile dir if it doesn't exist"""
222 ).tag(config=True)
233 ).tag(config=True)
223
234
224 config_files = List(Unicode())
235 config_files = List(Unicode())
225 @default('config_files')
236 @default('config_files')
226 def _config_files_default(self):
237 def _config_files_default(self):
227 return [self.config_file_name]
238 return [self.config_file_name]
228
239
229 copy_config_files = Bool(False,
240 copy_config_files = Bool(False,
230 help="""Whether to install the default config files into the profile dir.
241 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
242 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,
243 profile, then they will be staged into the new directory. Otherwise,
233 default config files will be automatically generated.
244 default config files will be automatically generated.
234 """).tag(config=True)
245 """).tag(config=True)
235
246
236 verbose_crash = Bool(False,
247 verbose_crash = Bool(False,
237 help="""Create a massive crash report when IPython encounters what may be an
248 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
249 internal error. The default is to append a short message to the
239 usual traceback""").tag(config=True)
250 usual traceback""").tag(config=True)
240
251
241 # The class to use as the crash handler.
252 # The class to use as the crash handler.
242 crash_handler_class = Type(crashhandler.CrashHandler)
253 crash_handler_class = Type(crashhandler.CrashHandler)
243
254
244 @catch_config_error
255 @catch_config_error
245 def __init__(self, **kwargs):
256 def __init__(self, **kwargs):
246 super(BaseIPythonApplication, self).__init__(**kwargs)
257 super(BaseIPythonApplication, self).__init__(**kwargs)
247 # ensure current working directory exists
258 # ensure current working directory exists
248 try:
259 try:
249 os.getcwd()
260 os.getcwd()
250 except:
261 except:
251 # exit if cwd doesn't exist
262 # exit if cwd doesn't exist
252 self.log.error("Current working directory doesn't exist.")
263 self.log.error("Current working directory doesn't exist.")
253 self.exit(1)
264 self.exit(1)
254
265
255 #-------------------------------------------------------------------------
266 #-------------------------------------------------------------------------
256 # Various stages of Application creation
267 # Various stages of Application creation
257 #-------------------------------------------------------------------------
268 #-------------------------------------------------------------------------
258
269
259 def init_crash_handler(self):
270 def init_crash_handler(self):
260 """Create a crash handler, typically setting sys.excepthook to it."""
271 """Create a crash handler, typically setting sys.excepthook to it."""
261 self.crash_handler = self.crash_handler_class(self)
272 self.crash_handler = self.crash_handler_class(self)
262 sys.excepthook = self.excepthook
273 sys.excepthook = self.excepthook
263 def unset_crashhandler():
274 def unset_crashhandler():
264 sys.excepthook = sys.__excepthook__
275 sys.excepthook = sys.__excepthook__
265 atexit.register(unset_crashhandler)
276 atexit.register(unset_crashhandler)
266
277
267 def excepthook(self, etype, evalue, tb):
278 def excepthook(self, etype, evalue, tb):
268 """this is sys.excepthook after init_crashhandler
279 """this is sys.excepthook after init_crashhandler
269
280
270 set self.verbose_crash=True to use our full crashhandler, instead of
281 set self.verbose_crash=True to use our full crashhandler, instead of
271 a regular traceback with a short message (crash_handler_lite)
282 a regular traceback with a short message (crash_handler_lite)
272 """
283 """
273
284
274 if self.verbose_crash:
285 if self.verbose_crash:
275 return self.crash_handler(etype, evalue, tb)
286 return self.crash_handler(etype, evalue, tb)
276 else:
287 else:
277 return crashhandler.crash_handler_lite(etype, evalue, tb)
288 return crashhandler.crash_handler_lite(etype, evalue, tb)
278
289
279 @observe('ipython_dir')
290 @observe('ipython_dir')
280 def _ipython_dir_changed(self, change):
291 def _ipython_dir_changed(self, change):
281 old = change['old']
292 old = change['old']
282 new = change['new']
293 new = change['new']
283 if old is not Undefined:
294 if old is not Undefined:
284 str_old = os.path.abspath(old)
295 str_old = os.path.abspath(old)
285 if str_old in sys.path:
296 if str_old in sys.path:
286 sys.path.remove(str_old)
297 sys.path.remove(str_old)
287 str_path = os.path.abspath(new)
298 if self.add_ipython_dir_to_sys_path:
288 sys.path.append(str_path)
299 str_path = os.path.abspath(new)
289 ensure_dir_exists(new)
300 sys.path.append(str_path)
290 readme = os.path.join(new, 'README')
301 ensure_dir_exists(new)
291 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
302 readme = os.path.join(new, "README")
292 if not os.path.exists(readme) and os.path.exists(readme_src):
303 readme_src = os.path.join(
293 shutil.copy(readme_src, readme)
304 get_ipython_package_dir(), "config", "profile", "README"
294 for d in ('extensions', 'nbextensions'):
305 )
295 path = os.path.join(new, d)
306 if not os.path.exists(readme) and os.path.exists(readme_src):
296 try:
307 shutil.copy(readme_src, readme)
297 ensure_dir_exists(path)
308 for d in ("extensions", "nbextensions"):
298 except OSError as e:
309 path = os.path.join(new, d)
299 # this will not be EEXIST
310 try:
300 self.log.error("couldn't create path %s: %s", path, e)
311 ensure_dir_exists(path)
301 self.log.debug("IPYTHONDIR set to: %s" % new)
312 except OSError as e:
313 # this will not be EEXIST
314 self.log.error("couldn't create path %s: %s", path, e)
315 self.log.debug("IPYTHONDIR set to: %s" % new)
302
316
303 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
317 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
304 """Load the config file.
318 """Load the config file.
305
319
306 By default, errors in loading config are handled, and a warning
320 By default, errors in loading config are handled, and a warning
307 printed on screen. For testing, the suppress_errors option is set
321 printed on screen. For testing, the suppress_errors option is set
308 to False, so errors will make tests fail.
322 to False, so errors will make tests fail.
309
323
310 `suppress_errors` default value is to be `None` in which case the
324 `suppress_errors` default value is to be `None` in which case the
311 behavior default to the one of `traitlets.Application`.
325 behavior default to the one of `traitlets.Application`.
312
326
313 The default value can be set :
327 The default value can be set :
314 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
328 - 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).
329 - 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.
330 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
317
331
318 Any other value are invalid, and will make IPython exit with a non-zero return code.
332 Any other value are invalid, and will make IPython exit with a non-zero return code.
319 """
333 """
320
334
321
335
322 self.log.debug("Searching path %s for config files", self.config_file_paths)
336 self.log.debug("Searching path %s for config files", self.config_file_paths)
323 base_config = 'ipython_config.py'
337 base_config = 'ipython_config.py'
324 self.log.debug("Attempting to load config file: %s" %
338 self.log.debug("Attempting to load config file: %s" %
325 base_config)
339 base_config)
326 try:
340 try:
327 if suppress_errors is not None:
341 if suppress_errors is not None:
328 old_value = Application.raise_config_file_errors
342 old_value = Application.raise_config_file_errors
329 Application.raise_config_file_errors = not suppress_errors;
343 Application.raise_config_file_errors = not suppress_errors;
330 Application.load_config_file(
344 Application.load_config_file(
331 self,
345 self,
332 base_config,
346 base_config,
333 path=self.config_file_paths
347 path=self.config_file_paths
334 )
348 )
335 except ConfigFileNotFound:
349 except ConfigFileNotFound:
336 # ignore errors loading parent
350 # ignore errors loading parent
337 self.log.debug("Config file %s not found", base_config)
351 self.log.debug("Config file %s not found", base_config)
338 pass
352 pass
339 if suppress_errors is not None:
353 if suppress_errors is not None:
340 Application.raise_config_file_errors = old_value
354 Application.raise_config_file_errors = old_value
341
355
342 for config_file_name in self.config_files:
356 for config_file_name in self.config_files:
343 if not config_file_name or config_file_name == base_config:
357 if not config_file_name or config_file_name == base_config:
344 continue
358 continue
345 self.log.debug("Attempting to load config file: %s" %
359 self.log.debug("Attempting to load config file: %s" %
346 self.config_file_name)
360 self.config_file_name)
347 try:
361 try:
348 Application.load_config_file(
362 Application.load_config_file(
349 self,
363 self,
350 config_file_name,
364 config_file_name,
351 path=self.config_file_paths
365 path=self.config_file_paths
352 )
366 )
353 except ConfigFileNotFound:
367 except ConfigFileNotFound:
354 # Only warn if the default config file was NOT being used.
368 # Only warn if the default config file was NOT being used.
355 if config_file_name in self.config_file_specified:
369 if config_file_name in self.config_file_specified:
356 msg = self.log.warning
370 msg = self.log.warning
357 else:
371 else:
358 msg = self.log.debug
372 msg = self.log.debug
359 msg("Config file not found, skipping: %s", config_file_name)
373 msg("Config file not found, skipping: %s", config_file_name)
360 except Exception:
374 except Exception:
361 # For testing purposes.
375 # For testing purposes.
362 if not suppress_errors:
376 if not suppress_errors:
363 raise
377 raise
364 self.log.warning("Error loading config file: %s" %
378 self.log.warning("Error loading config file: %s" %
365 self.config_file_name, exc_info=True)
379 self.config_file_name, exc_info=True)
366
380
367 def init_profile_dir(self):
381 def init_profile_dir(self):
368 """initialize the profile dir"""
382 """initialize the profile dir"""
369 self._in_init_profile_dir = True
383 self._in_init_profile_dir = True
370 if self.profile_dir is not None:
384 if self.profile_dir is not None:
371 # already ran
385 # already ran
372 return
386 return
373 if 'ProfileDir.location' not in self.config:
387 if 'ProfileDir.location' not in self.config:
374 # location not specified, find by profile name
388 # location not specified, find by profile name
375 try:
389 try:
376 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
390 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
377 except ProfileDirError:
391 except ProfileDirError:
378 # not found, maybe create it (always create default profile)
392 # not found, maybe create it (always create default profile)
379 if self.auto_create or self.profile == 'default':
393 if self.auto_create or self.profile == 'default':
380 try:
394 try:
381 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
395 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
382 except ProfileDirError:
396 except ProfileDirError:
383 self.log.fatal("Could not create profile: %r"%self.profile)
397 self.log.fatal("Could not create profile: %r"%self.profile)
384 self.exit(1)
398 self.exit(1)
385 else:
399 else:
386 self.log.info("Created profile dir: %r"%p.location)
400 self.log.info("Created profile dir: %r"%p.location)
387 else:
401 else:
388 self.log.fatal("Profile %r not found."%self.profile)
402 self.log.fatal("Profile %r not found."%self.profile)
389 self.exit(1)
403 self.exit(1)
390 else:
404 else:
391 self.log.debug(f"Using existing profile dir: {p.location!r}")
405 self.log.debug(f"Using existing profile dir: {p.location!r}")
392 else:
406 else:
393 location = self.config.ProfileDir.location
407 location = self.config.ProfileDir.location
394 # location is fully specified
408 # location is fully specified
395 try:
409 try:
396 p = ProfileDir.find_profile_dir(location, self.config)
410 p = ProfileDir.find_profile_dir(location, self.config)
397 except ProfileDirError:
411 except ProfileDirError:
398 # not found, maybe create it
412 # not found, maybe create it
399 if self.auto_create:
413 if self.auto_create:
400 try:
414 try:
401 p = ProfileDir.create_profile_dir(location, self.config)
415 p = ProfileDir.create_profile_dir(location, self.config)
402 except ProfileDirError:
416 except ProfileDirError:
403 self.log.fatal("Could not create profile directory: %r"%location)
417 self.log.fatal("Could not create profile directory: %r"%location)
404 self.exit(1)
418 self.exit(1)
405 else:
419 else:
406 self.log.debug("Creating new profile dir: %r"%location)
420 self.log.debug("Creating new profile dir: %r"%location)
407 else:
421 else:
408 self.log.fatal("Profile directory %r not found."%location)
422 self.log.fatal("Profile directory %r not found."%location)
409 self.exit(1)
423 self.exit(1)
410 else:
424 else:
411 self.log.debug(f"Using existing profile dir: {p.location!r}")
425 self.log.debug(f"Using existing profile dir: {p.location!r}")
412 # if profile_dir is specified explicitly, set profile name
426 # if profile_dir is specified explicitly, set profile name
413 dir_name = os.path.basename(p.location)
427 dir_name = os.path.basename(p.location)
414 if dir_name.startswith('profile_'):
428 if dir_name.startswith('profile_'):
415 self.profile = dir_name[8:]
429 self.profile = dir_name[8:]
416
430
417 self.profile_dir = p
431 self.profile_dir = p
418 self.config_file_paths.append(p.location)
432 self.config_file_paths.append(p.location)
419 self._in_init_profile_dir = False
433 self._in_init_profile_dir = False
420
434
421 def init_config_files(self):
435 def init_config_files(self):
422 """[optionally] copy default config files into profile dir."""
436 """[optionally] copy default config files into profile dir."""
423 self.config_file_paths.extend(ENV_CONFIG_DIRS)
437 self.config_file_paths.extend(ENV_CONFIG_DIRS)
424 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
438 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
425 # copy config files
439 # copy config files
426 path = Path(self.builtin_profile_dir)
440 path = Path(self.builtin_profile_dir)
427 if self.copy_config_files:
441 if self.copy_config_files:
428 src = self.profile
442 src = self.profile
429
443
430 cfg = self.config_file_name
444 cfg = self.config_file_name
431 if path and (path / cfg).exists():
445 if path and (path / cfg).exists():
432 self.log.warning(
446 self.log.warning(
433 "Staging %r from %s into %r [overwrite=%s]"
447 "Staging %r from %s into %r [overwrite=%s]"
434 % (cfg, src, self.profile_dir.location, self.overwrite)
448 % (cfg, src, self.profile_dir.location, self.overwrite)
435 )
449 )
436 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
450 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
437 else:
451 else:
438 self.stage_default_config_file()
452 self.stage_default_config_file()
439 else:
453 else:
440 # Still stage *bundled* config files, but not generated ones
454 # Still stage *bundled* config files, but not generated ones
441 # This is necessary for `ipython profile=sympy` to load the profile
455 # This is necessary for `ipython profile=sympy` to load the profile
442 # on the first go
456 # on the first go
443 files = path.glob("*.py")
457 files = path.glob("*.py")
444 for fullpath in files:
458 for fullpath in files:
445 cfg = fullpath.name
459 cfg = fullpath.name
446 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
460 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
447 # file was copied
461 # file was copied
448 self.log.warning("Staging bundled %s from %s into %r"%(
462 self.log.warning("Staging bundled %s from %s into %r"%(
449 cfg, self.profile, self.profile_dir.location)
463 cfg, self.profile, self.profile_dir.location)
450 )
464 )
451
465
452
466
453 def stage_default_config_file(self):
467 def stage_default_config_file(self):
454 """auto generate default config file, and stage it into the profile."""
468 """auto generate default config file, and stage it into the profile."""
455 s = self.generate_config_file()
469 s = self.generate_config_file()
456 config_file = Path(self.profile_dir.location) / self.config_file_name
470 config_file = Path(self.profile_dir.location) / self.config_file_name
457 if self.overwrite or not config_file.exists():
471 if self.overwrite or not config_file.exists():
458 self.log.warning("Generating default config file: %r" % (config_file))
472 self.log.warning("Generating default config file: %r" % (config_file))
459 config_file.write_text(s)
473 config_file.write_text(s)
460
474
461 @catch_config_error
475 @catch_config_error
462 def initialize(self, argv=None):
476 def initialize(self, argv=None):
463 # don't hook up crash handler before parsing command-line
477 # don't hook up crash handler before parsing command-line
464 self.parse_command_line(argv)
478 self.parse_command_line(argv)
465 self.init_crash_handler()
479 self.init_crash_handler()
466 if self.subapp is not None:
480 if self.subapp is not None:
467 # stop here if subapp is taking over
481 # stop here if subapp is taking over
468 return
482 return
469 # save a copy of CLI config to re-load after config files
483 # save a copy of CLI config to re-load after config files
470 # so that it has highest priority
484 # so that it has highest priority
471 cl_config = deepcopy(self.config)
485 cl_config = deepcopy(self.config)
472 self.init_profile_dir()
486 self.init_profile_dir()
473 self.init_config_files()
487 self.init_config_files()
474 self.load_config_file()
488 self.load_config_file()
475 # enforce cl-opts override configfile opts:
489 # enforce cl-opts override configfile opts:
476 self.update_config(cl_config)
490 self.update_config(cl_config)
General Comments 0
You need to be logged in to leave comments. Login now