##// END OF EJS Templates
Only copy files in startup dir if we just created it. (#14497)...
M Bussonnier -
r28823:78fea5f8 merge
parent child Browse files
Show More
@@ -1,225 +1,244 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """An object for managing IPython profile directories."""
2 """An object for managing IPython profile directories."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import shutil
8 import shutil
9 import errno
9 import errno
10 from pathlib import Path
10 from pathlib import Path
11
11
12 from traitlets.config.configurable import LoggingConfigurable
12 from traitlets.config.configurable import LoggingConfigurable
13 from ..paths import get_ipython_package_dir
13 from ..paths import get_ipython_package_dir
14 from ..utils.path import expand_path, ensure_dir_exists
14 from ..utils.path import expand_path, ensure_dir_exists
15 from traitlets import Unicode, Bool, observe
15 from traitlets import Unicode, Bool, observe
16
16
17 from typing import Optional
18
17 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
18 # Module errors
20 # Module errors
19 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
20
22
21 class ProfileDirError(Exception):
23 class ProfileDirError(Exception):
22 pass
24 pass
23
25
24
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Class for managing profile directories
28 # Class for managing profile directories
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28
30
29 class ProfileDir(LoggingConfigurable):
31 class ProfileDir(LoggingConfigurable):
30 """An object to manage the profile directory and its resources.
32 """An object to manage the profile directory and its resources.
31
33
32 The profile directory is used by all IPython applications, to manage
34 The profile directory is used by all IPython applications, to manage
33 configuration, logging and security.
35 configuration, logging and security.
34
36
35 This object knows how to find, create and manage these directories. This
37 This object knows how to find, create and manage these directories. This
36 should be used by any code that wants to handle profiles.
38 should be used by any code that wants to handle profiles.
37 """
39 """
38
40
39 security_dir_name = Unicode('security')
41 security_dir_name = Unicode('security')
40 log_dir_name = Unicode('log')
42 log_dir_name = Unicode('log')
41 startup_dir_name = Unicode('startup')
43 startup_dir_name = Unicode('startup')
42 pid_dir_name = Unicode('pid')
44 pid_dir_name = Unicode('pid')
43 static_dir_name = Unicode('static')
45 static_dir_name = Unicode('static')
44 security_dir = Unicode(u'')
46 security_dir = Unicode(u'')
45 log_dir = Unicode(u'')
47 log_dir = Unicode(u'')
46 startup_dir = Unicode(u'')
48 startup_dir = Unicode(u'')
47 pid_dir = Unicode(u'')
49 pid_dir = Unicode(u'')
48 static_dir = Unicode(u'')
50 static_dir = Unicode(u'')
49
51
50 location = Unicode(u'',
52 location = Unicode(u'',
51 help="""Set the profile location directly. This overrides the logic used by the
53 help="""Set the profile location directly. This overrides the logic used by the
52 `profile` option.""",
54 `profile` option.""",
53 ).tag(config=True)
55 ).tag(config=True)
54
56
55 _location_isset = Bool(False) # flag for detecting multiply set location
57 _location_isset = Bool(False) # flag for detecting multiply set location
56 @observe('location')
58 @observe('location')
57 def _location_changed(self, change):
59 def _location_changed(self, change):
58 if self._location_isset:
60 if self._location_isset:
59 raise RuntimeError("Cannot set profile location more than once.")
61 raise RuntimeError("Cannot set profile location more than once.")
60 self._location_isset = True
62 self._location_isset = True
61 new = change['new']
63 new = change['new']
62 ensure_dir_exists(new)
64 ensure_dir_exists(new)
63
65
64 # ensure config files exist:
66 # ensure config files exist:
65 self.security_dir = os.path.join(new, self.security_dir_name)
67 self.security_dir = os.path.join(new, self.security_dir_name)
66 self.log_dir = os.path.join(new, self.log_dir_name)
68 self.log_dir = os.path.join(new, self.log_dir_name)
67 self.startup_dir = os.path.join(new, self.startup_dir_name)
69 self.startup_dir = os.path.join(new, self.startup_dir_name)
68 self.pid_dir = os.path.join(new, self.pid_dir_name)
70 self.pid_dir = os.path.join(new, self.pid_dir_name)
69 self.static_dir = os.path.join(new, self.static_dir_name)
71 self.static_dir = os.path.join(new, self.static_dir_name)
70 self.check_dirs()
72 self.check_dirs()
71
73
72 def _mkdir(self, path, mode=None):
74 def _mkdir(self, path: str, mode: Optional[int] = None) -> bool:
73 """ensure a directory exists at a given path
75 """ensure a directory exists at a given path
74
76
75 This is a version of os.mkdir, with the following differences:
77 This is a version of os.mkdir, with the following differences:
76
78
77 - returns True if it created the directory, False otherwise
79 - returns whether the directory has been created or not.
78 - ignores EEXIST, protecting against race conditions where
80 - ignores EEXIST, protecting against race conditions where
79 the dir may have been created in between the check and
81 the dir may have been created in between the check and
80 the creation
82 the creation
81 - sets permissions if requested and the dir already exists
83 - sets permissions if requested and the dir already exists
84
85 Parameters
86 ----------
87 path: str
88 path of the dir to create
89 mode: int
90 see `mode` of `os.mkdir`
91
92 Returns
93 -------
94 bool:
95 returns True if it created the directory, False otherwise
82 """
96 """
97
83 if os.path.exists(path):
98 if os.path.exists(path):
84 if mode and os.stat(path).st_mode != mode:
99 if mode and os.stat(path).st_mode != mode:
85 try:
100 try:
86 os.chmod(path, mode)
101 os.chmod(path, mode)
87 except OSError:
102 except OSError:
88 self.log.warning(
103 self.log.warning(
89 "Could not set permissions on %s",
104 "Could not set permissions on %s",
90 path
105 path
91 )
106 )
92 return False
107 return False
93 try:
108 try:
94 if mode:
109 if mode:
95 os.mkdir(path, mode)
110 os.mkdir(path, mode)
96 else:
111 else:
97 os.mkdir(path)
112 os.mkdir(path)
98 except OSError as e:
113 except OSError as e:
99 if e.errno == errno.EEXIST:
114 if e.errno == errno.EEXIST:
100 return False
115 return False
101 else:
116 else:
102 raise
117 raise
103
118
104 return True
119 return True
105
120
106 @observe('log_dir')
121 @observe('log_dir')
107 def check_log_dir(self, change=None):
122 def check_log_dir(self, change=None):
108 self._mkdir(self.log_dir)
123 self._mkdir(self.log_dir)
109
124
110 @observe('startup_dir')
125 @observe('startup_dir')
111 def check_startup_dir(self, change=None):
126 def check_startup_dir(self, change=None):
112 self._mkdir(self.startup_dir)
127 if self._mkdir(self.startup_dir):
113
128 readme = os.path.join(self.startup_dir, "README")
114 readme = os.path.join(self.startup_dir, 'README')
129 src = os.path.join(
115 src = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'README_STARTUP')
130 get_ipython_package_dir(), "core", "profile", "README_STARTUP"
116
131 )
117 if not os.path.exists(src):
132
118 self.log.warning("Could not copy README_STARTUP to startup dir. Source file %s does not exist.", src)
133 if os.path.exists(src):
119
134 if not os.path.exists(readme):
120 if os.path.exists(src) and not os.path.exists(readme):
135 shutil.copy(src, readme)
121 shutil.copy(src, readme)
136 else:
137 self.log.warning(
138 "Could not copy README_STARTUP to startup dir. Source file %s does not exist.",
139 src,
140 )
122
141
123 @observe('security_dir')
142 @observe('security_dir')
124 def check_security_dir(self, change=None):
143 def check_security_dir(self, change=None):
125 self._mkdir(self.security_dir, 0o40700)
144 self._mkdir(self.security_dir, 0o40700)
126
145
127 @observe('pid_dir')
146 @observe('pid_dir')
128 def check_pid_dir(self, change=None):
147 def check_pid_dir(self, change=None):
129 self._mkdir(self.pid_dir, 0o40700)
148 self._mkdir(self.pid_dir, 0o40700)
130
149
131 def check_dirs(self):
150 def check_dirs(self):
132 self.check_security_dir()
151 self.check_security_dir()
133 self.check_log_dir()
152 self.check_log_dir()
134 self.check_pid_dir()
153 self.check_pid_dir()
135 self.check_startup_dir()
154 self.check_startup_dir()
136
155
137 def copy_config_file(self, config_file: str, path: Path, overwrite=False) -> bool:
156 def copy_config_file(self, config_file: str, path: Path, overwrite=False) -> bool:
138 """Copy a default config file into the active profile directory.
157 """Copy a default config file into the active profile directory.
139
158
140 Default configuration files are kept in :mod:`IPython.core.profile`.
159 Default configuration files are kept in :mod:`IPython.core.profile`.
141 This function moves these from that location to the working profile
160 This function moves these from that location to the working profile
142 directory.
161 directory.
143 """
162 """
144 dst = Path(os.path.join(self.location, config_file))
163 dst = Path(os.path.join(self.location, config_file))
145 if dst.exists() and not overwrite:
164 if dst.exists() and not overwrite:
146 return False
165 return False
147 if path is None:
166 if path is None:
148 path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default')
167 path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default')
149 assert isinstance(path, Path)
168 assert isinstance(path, Path)
150 src = path / config_file
169 src = path / config_file
151 shutil.copy(src, dst)
170 shutil.copy(src, dst)
152 return True
171 return True
153
172
154 @classmethod
173 @classmethod
155 def create_profile_dir(cls, profile_dir, config=None):
174 def create_profile_dir(cls, profile_dir, config=None):
156 """Create a new profile directory given a full path.
175 """Create a new profile directory given a full path.
157
176
158 Parameters
177 Parameters
159 ----------
178 ----------
160 profile_dir : str
179 profile_dir : str
161 The full path to the profile directory. If it does exist, it will
180 The full path to the profile directory. If it does exist, it will
162 be used. If not, it will be created.
181 be used. If not, it will be created.
163 """
182 """
164 return cls(location=profile_dir, config=config)
183 return cls(location=profile_dir, config=config)
165
184
166 @classmethod
185 @classmethod
167 def create_profile_dir_by_name(cls, path, name=u'default', config=None):
186 def create_profile_dir_by_name(cls, path, name=u'default', config=None):
168 """Create a profile dir by profile name and path.
187 """Create a profile dir by profile name and path.
169
188
170 Parameters
189 Parameters
171 ----------
190 ----------
172 path : unicode
191 path : unicode
173 The path (directory) to put the profile directory in.
192 The path (directory) to put the profile directory in.
174 name : unicode
193 name : unicode
175 The name of the profile. The name of the profile directory will
194 The name of the profile. The name of the profile directory will
176 be "profile_<profile>".
195 be "profile_<profile>".
177 """
196 """
178 if not os.path.isdir(path):
197 if not os.path.isdir(path):
179 raise ProfileDirError('Directory not found: %s' % path)
198 raise ProfileDirError('Directory not found: %s' % path)
180 profile_dir = os.path.join(path, u'profile_' + name)
199 profile_dir = os.path.join(path, u'profile_' + name)
181 return cls(location=profile_dir, config=config)
200 return cls(location=profile_dir, config=config)
182
201
183 @classmethod
202 @classmethod
184 def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
203 def find_profile_dir_by_name(cls, ipython_dir, name=u'default', config=None):
185 """Find an existing profile dir by profile name, return its ProfileDir.
204 """Find an existing profile dir by profile name, return its ProfileDir.
186
205
187 This searches through a sequence of paths for a profile dir. If it
206 This searches through a sequence of paths for a profile dir. If it
188 is not found, a :class:`ProfileDirError` exception will be raised.
207 is not found, a :class:`ProfileDirError` exception will be raised.
189
208
190 The search path algorithm is:
209 The search path algorithm is:
191 1. ``os.getcwd()`` # removed for security reason.
210 1. ``os.getcwd()`` # removed for security reason.
192 2. ``ipython_dir``
211 2. ``ipython_dir``
193
212
194 Parameters
213 Parameters
195 ----------
214 ----------
196 ipython_dir : unicode or str
215 ipython_dir : unicode or str
197 The IPython directory to use.
216 The IPython directory to use.
198 name : unicode or str
217 name : unicode or str
199 The name of the profile. The name of the profile directory
218 The name of the profile. The name of the profile directory
200 will be "profile_<profile>".
219 will be "profile_<profile>".
201 """
220 """
202 dirname = u'profile_' + name
221 dirname = u'profile_' + name
203 paths = [ipython_dir]
222 paths = [ipython_dir]
204 for p in paths:
223 for p in paths:
205 profile_dir = os.path.join(p, dirname)
224 profile_dir = os.path.join(p, dirname)
206 if os.path.isdir(profile_dir):
225 if os.path.isdir(profile_dir):
207 return cls(location=profile_dir, config=config)
226 return cls(location=profile_dir, config=config)
208 else:
227 else:
209 raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
228 raise ProfileDirError('Profile directory not found in paths: %s' % dirname)
210
229
211 @classmethod
230 @classmethod
212 def find_profile_dir(cls, profile_dir, config=None):
231 def find_profile_dir(cls, profile_dir, config=None):
213 """Find/create a profile dir and return its ProfileDir.
232 """Find/create a profile dir and return its ProfileDir.
214
233
215 This will create the profile directory if it doesn't exist.
234 This will create the profile directory if it doesn't exist.
216
235
217 Parameters
236 Parameters
218 ----------
237 ----------
219 profile_dir : unicode or str
238 profile_dir : unicode or str
220 The path of the profile directory.
239 The path of the profile directory.
221 """
240 """
222 profile_dir = expand_path(profile_dir)
241 profile_dir = expand_path(profile_dir)
223 if not os.path.isdir(profile_dir):
242 if not os.path.isdir(profile_dir):
224 raise ProfileDirError('Profile directory not found: %s' % profile_dir)
243 raise ProfileDirError('Profile directory not found: %s' % profile_dir)
225 return cls(location=profile_dir, config=config)
244 return cls(location=profile_dir, config=config)
General Comments 0
You need to be logged in to leave comments. Login now