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