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