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