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