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