##// END OF EJS Templates
Update path.py...
jason gors -
Show More
@@ -1,586 +1,586 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for path handling.
3 Utilities for path handling.
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import os
9 import os
10 import sys
10 import sys
11 import errno
11 import errno
12 import shutil
12 import shutil
13 import random
13 import random
14 import tempfile
14 import tempfile
15 import glob
15 import glob
16 from warnings import warn
16 from warnings import warn
17 from hashlib import md5
17 from hashlib import md5
18
18
19 import IPython
19 import IPython
20 from IPython.testing.skipdoctest import skip_doctest
20 from IPython.testing.skipdoctest import skip_doctest
21 from IPython.utils.process import system
21 from IPython.utils.process import system
22 from IPython.utils.importstring import import_item
22 from IPython.utils.importstring import import_item
23 from IPython.utils import py3compat
23 from IPython.utils import py3compat
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Code
25 # Code
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 fs_encoding = sys.getfilesystemencoding()
28 fs_encoding = sys.getfilesystemencoding()
29
29
30 def _get_long_path_name(path):
30 def _get_long_path_name(path):
31 """Dummy no-op."""
31 """Dummy no-op."""
32 return path
32 return path
33
33
34 def _writable_dir(path):
34 def _writable_dir(path):
35 """Whether `path` is a directory, to which the user has write access."""
35 """Whether `path` is a directory, to which the user has write access."""
36 return os.path.isdir(path) and os.access(path, os.W_OK)
36 return os.path.isdir(path) and os.access(path, os.W_OK)
37
37
38 if sys.platform == 'win32':
38 if sys.platform == 'win32':
39 @skip_doctest
39 @skip_doctest
40 def _get_long_path_name(path):
40 def _get_long_path_name(path):
41 """Get a long path name (expand ~) on Windows using ctypes.
41 """Get a long path name (expand ~) on Windows using ctypes.
42
42
43 Examples
43 Examples
44 --------
44 --------
45
45
46 >>> get_long_path_name('c:\\docume~1')
46 >>> get_long_path_name('c:\\docume~1')
47 u'c:\\\\Documents and Settings'
47 u'c:\\\\Documents and Settings'
48
48
49 """
49 """
50 try:
50 try:
51 import ctypes
51 import ctypes
52 except ImportError:
52 except ImportError:
53 raise ImportError('you need to have ctypes installed for this to work')
53 raise ImportError('you need to have ctypes installed for this to work')
54 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
54 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
55 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
55 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
56 ctypes.c_uint ]
56 ctypes.c_uint ]
57
57
58 buf = ctypes.create_unicode_buffer(260)
58 buf = ctypes.create_unicode_buffer(260)
59 rv = _GetLongPathName(path, buf, 260)
59 rv = _GetLongPathName(path, buf, 260)
60 if rv == 0 or rv > 260:
60 if rv == 0 or rv > 260:
61 return path
61 return path
62 else:
62 else:
63 return buf.value
63 return buf.value
64
64
65
65
66 def get_long_path_name(path):
66 def get_long_path_name(path):
67 """Expand a path into its long form.
67 """Expand a path into its long form.
68
68
69 On Windows this expands any ~ in the paths. On other platforms, it is
69 On Windows this expands any ~ in the paths. On other platforms, it is
70 a null operation.
70 a null operation.
71 """
71 """
72 return _get_long_path_name(path)
72 return _get_long_path_name(path)
73
73
74
74
75 def unquote_filename(name, win32=(sys.platform=='win32')):
75 def unquote_filename(name, win32=(sys.platform=='win32')):
76 """ On Windows, remove leading and trailing quotes from filenames.
76 """ On Windows, remove leading and trailing quotes from filenames.
77 """
77 """
78 if win32:
78 if win32:
79 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
79 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
80 name = name[1:-1]
80 name = name[1:-1]
81 return name
81 return name
82
82
83 def compress_user(path):
83 def compress_user(path):
84 """Reverse of :func:`os.path.expanduser`
84 """Reverse of :func:`os.path.expanduser`
85 """
85 """
86 home = os.path.expanduser('~')
86 home = os.path.expanduser('~')
87 if path.startswith(home):
87 if path.startswith(home):
88 path = "~" + path[len(home):]
88 path = "~" + path[len(home):]
89 return path
89 return path
90
90
91 def get_py_filename(name, force_win32=None):
91 def get_py_filename(name, force_win32=None):
92 """Return a valid python filename in the current directory.
92 """Return a valid python filename in the current directory.
93
93
94 If the given name is not a file, it adds '.py' and searches again.
94 If the given name is not a file, it adds '.py' and searches again.
95 Raises IOError with an informative message if the file isn't found.
95 Raises IOError with an informative message if the file isn't found.
96
96
97 On Windows, apply Windows semantics to the filename. In particular, remove
97 On Windows, apply Windows semantics to the filename. In particular, remove
98 any quoting that has been applied to it. This option can be forced for
98 any quoting that has been applied to it. This option can be forced for
99 testing purposes.
99 testing purposes.
100 """
100 """
101
101
102 name = os.path.expanduser(name)
102 name = os.path.expanduser(name)
103 if force_win32 is None:
103 if force_win32 is None:
104 win32 = (sys.platform == 'win32')
104 win32 = (sys.platform == 'win32')
105 else:
105 else:
106 win32 = force_win32
106 win32 = force_win32
107 name = unquote_filename(name, win32=win32)
107 name = unquote_filename(name, win32=win32)
108 if not os.path.isfile(name) and not name.endswith('.py'):
108 if not os.path.isfile(name) and not name.endswith('.py'):
109 name += '.py'
109 name += '.py'
110 if os.path.isfile(name):
110 if os.path.isfile(name):
111 return name
111 return name
112 else:
112 else:
113 raise IOError('File `%r` not found.' % name)
113 raise IOError('File `%r` not found.' % name)
114
114
115
115
116 def filefind(filename, path_dirs=None):
116 def filefind(filename, path_dirs=None):
117 """Find a file by looking through a sequence of paths.
117 """Find a file by looking through a sequence of paths.
118
118
119 This iterates through a sequence of paths looking for a file and returns
119 This iterates through a sequence of paths looking for a file and returns
120 the full, absolute path of the first occurence of the file. If no set of
120 the full, absolute path of the first occurence of the file. If no set of
121 path dirs is given, the filename is tested as is, after running through
121 path dirs is given, the filename is tested as is, after running through
122 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
122 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
123
123
124 filefind('myfile.txt')
124 filefind('myfile.txt')
125
125
126 will find the file in the current working dir, but::
126 will find the file in the current working dir, but::
127
127
128 filefind('~/myfile.txt')
128 filefind('~/myfile.txt')
129
129
130 Will find the file in the users home directory. This function does not
130 Will find the file in the users home directory. This function does not
131 automatically try any paths, such as the cwd or the user's home directory.
131 automatically try any paths, such as the cwd or the user's home directory.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 filename : str
135 filename : str
136 The filename to look for.
136 The filename to look for.
137 path_dirs : str, None or sequence of str
137 path_dirs : str, None or sequence of str
138 The sequence of paths to look for the file in. If None, the filename
138 The sequence of paths to look for the file in. If None, the filename
139 need to be absolute or be in the cwd. If a string, the string is
139 need to be absolute or be in the cwd. If a string, the string is
140 put into a sequence and the searched. If a sequence, walk through
140 put into a sequence and the searched. If a sequence, walk through
141 each element and join with ``filename``, calling :func:`expandvars`
141 each element and join with ``filename``, calling :func:`expandvars`
142 and :func:`expanduser` before testing for existence.
142 and :func:`expanduser` before testing for existence.
143
143
144 Returns
144 Returns
145 -------
145 -------
146 Raises :exc:`IOError` or returns absolute path to file.
146 Raises :exc:`IOError` or returns absolute path to file.
147 """
147 """
148
148
149 # If paths are quoted, abspath gets confused, strip them...
149 # If paths are quoted, abspath gets confused, strip them...
150 filename = filename.strip('"').strip("'")
150 filename = filename.strip('"').strip("'")
151 # If the input is an absolute path, just check it exists
151 # If the input is an absolute path, just check it exists
152 if os.path.isabs(filename) and os.path.isfile(filename):
152 if os.path.isabs(filename) and os.path.isfile(filename):
153 return filename
153 return filename
154
154
155 if path_dirs is None:
155 if path_dirs is None:
156 path_dirs = ("",)
156 path_dirs = ("",)
157 elif isinstance(path_dirs, py3compat.string_types):
157 elif isinstance(path_dirs, py3compat.string_types):
158 path_dirs = (path_dirs,)
158 path_dirs = (path_dirs,)
159
159
160 for path in path_dirs:
160 for path in path_dirs:
161 if path == '.': path = py3compat.getcwd()
161 if path == '.': path = py3compat.getcwd()
162 testname = expand_path(os.path.join(path, filename))
162 testname = expand_path(os.path.join(path, filename))
163 if os.path.isfile(testname):
163 if os.path.isfile(testname):
164 return os.path.abspath(testname)
164 return os.path.abspath(testname)
165
165
166 raise IOError("File %r does not exist in any of the search paths: %r" %
166 raise IOError("File %r does not exist in any of the search paths: %r" %
167 (filename, path_dirs) )
167 (filename, path_dirs) )
168
168
169
169
170 class HomeDirError(Exception):
170 class HomeDirError(Exception):
171 pass
171 pass
172
172
173
173
174 def get_home_dir(require_writable=False):
174 def get_home_dir(require_writable=False):
175 """Return the 'home' directory, as a unicode string.
175 """Return the 'home' directory, as a unicode string.
176
176
177 Uses os.path.expanduser('~'), and checks for writability.
177 Uses os.path.expanduser('~'), and checks for writability.
178
178
179 See stdlib docs for how this is determined.
179 See stdlib docs for how this is determined.
180 $HOME is first priority on *ALL* platforms.
180 $HOME is first priority on *ALL* platforms.
181
181
182 Parameters
182 Parameters
183 ----------
183 ----------
184
184
185 require_writable : bool [default: False]
185 require_writable : bool [default: False]
186 if True:
186 if True:
187 guarantees the return value is a writable directory, otherwise
187 guarantees the return value is a writable directory, otherwise
188 raises HomeDirError
188 raises HomeDirError
189 if False:
189 if False:
190 The path is resolved, but it is not guaranteed to exist or be writable.
190 The path is resolved, but it is not guaranteed to exist or be writable.
191 """
191 """
192
192
193 homedir = os.path.expanduser('~')
193 homedir = os.path.expanduser('~')
194 # Next line will make things work even when /home/ is a symlink to
194 # Next line will make things work even when /home/ is a symlink to
195 # /usr/home as it is on FreeBSD, for example
195 # /usr/home as it is on FreeBSD, for example
196 homedir = os.path.realpath(homedir)
196 homedir = os.path.realpath(homedir)
197
197
198 if not _writable_dir(homedir) and os.name == 'nt':
198 if not _writable_dir(homedir) and os.name == 'nt':
199 # expanduser failed, use the registry to get the 'My Documents' folder.
199 # expanduser failed, use the registry to get the 'My Documents' folder.
200 try:
200 try:
201 try:
201 try:
202 import winreg as wreg # Py 3
202 import winreg as wreg # Py 3
203 except ImportError:
203 except ImportError:
204 import _winreg as wreg # Py 2
204 import _winreg as wreg # Py 2
205 key = wreg.OpenKey(
205 key = wreg.OpenKey(
206 wreg.HKEY_CURRENT_USER,
206 wreg.HKEY_CURRENT_USER,
207 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
207 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
208 )
208 )
209 homedir = wreg.QueryValueEx(key,'Personal')[0]
209 homedir = wreg.QueryValueEx(key,'Personal')[0]
210 key.Close()
210 key.Close()
211 except:
211 except:
212 pass
212 pass
213
213
214 if (not require_writable) or _writable_dir(homedir):
214 if (not require_writable) or _writable_dir(homedir):
215 return py3compat.cast_unicode(homedir, fs_encoding)
215 return py3compat.cast_unicode(homedir, fs_encoding)
216 else:
216 else:
217 raise HomeDirError('%s is not a writable dir, '
217 raise HomeDirError('%s is not a writable dir, '
218 'set $HOME environment variable to override' % homedir)
218 'set $HOME environment variable to override' % homedir)
219
219
220 def get_xdg_dir():
220 def get_xdg_dir():
221 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
221 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
222
222
223 This is only for non-OS X posix (Linux,Unix,etc.) systems.
223 This is only for non-OS X posix (Linux,Unix,etc.) systems.
224 """
224 """
225
225
226 env = os.environ
226 env = os.environ
227
227
228 if os.name == 'posix' and sys.platform != 'darwin':
228 if os.name == 'posix' and sys.platform != 'darwin':
229 # Linux, Unix, AIX, etc.
229 # Linux, Unix, AIX, etc.
230 # use ~/.config if empty OR not set
230 # use ~/.config if empty OR not set
231 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
231 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
232 if xdg and _writable_dir(xdg):
232 if xdg and _writable_dir(xdg):
233 return py3compat.cast_unicode(xdg, fs_encoding)
233 return py3compat.cast_unicode(xdg, fs_encoding)
234
234
235 return None
235 return None
236
236
237
237
238 def get_xdg_cache_dir():
238 def get_xdg_cache_dir():
239 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
239 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
240
240
241 This is only for non-OS X posix (Linux,Unix,etc.) systems.
241 This is only for non-OS X posix (Linux,Unix,etc.) systems.
242 """
242 """
243
243
244 env = os.environ
244 env = os.environ
245
245
246 if os.name == 'posix' and sys.platform != 'darwin':
246 if os.name == 'posix' and sys.platform != 'darwin':
247 # Linux, Unix, AIX, etc.
247 # Linux, Unix, AIX, etc.
248 # use ~/.cache if empty OR not set
248 # use ~/.cache if empty OR not set
249 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
249 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
250 if xdg and _writable_dir(xdg):
250 if xdg and _writable_dir(xdg):
251 return py3compat.cast_unicode(xdg, fs_encoding)
251 return py3compat.cast_unicode(xdg, fs_encoding)
252
252
253 return None
253 return None
254
254
255
255
256 def get_ipython_dir():
256 def get_ipython_dir():
257 """Get the IPython directory for this platform and user.
257 """Get the IPython directory for this platform and user.
258
258
259 This uses the logic in `get_home_dir` to find the home directory
259 This uses the logic in `get_home_dir` to find the home directory
260 and then adds .ipython to the end of the path.
260 and then adds .ipython to the end of the path.
261 """
261 """
262
262
263 env = os.environ
263 env = os.environ
264 pjoin = os.path.join
264 pjoin = os.path.join
265
265
266
266
267 ipdir_def = '.ipython'
267 ipdir_def = '.ipython'
268
268
269 home_dir = get_home_dir()
269 home_dir = get_home_dir()
270 xdg_dir = get_xdg_dir()
270 xdg_dir = get_xdg_dir()
271
271
272 # import pdb; pdb.set_trace() # dbg
272 # import pdb; pdb.set_trace() # dbg
273 if 'IPYTHON_DIR' in env:
273 if 'IPYTHON_DIR' in env:
274 warn('The environment variable IPYTHON_DIR is deprecated. '
274 warn('The environment variable IPYTHON_DIR is deprecated. '
275 'Please use IPYTHONDIR instead.')
275 'Please use IPYTHONDIR instead.')
276 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
276 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
277 if ipdir is None:
277 if ipdir is None:
278 # not set explicitly, use ~/.ipython
278 # not set explicitly, use ~/.ipython
279 ipdir = pjoin(home_dir, ipdir_def)
279 ipdir = pjoin(home_dir, ipdir_def)
280 if xdg_dir:
280 if xdg_dir:
281 # Several IPython versions (up to 1.x) defaulted to .config/ipython
281 # Several IPython versions (up to 1.x) defaulted to .config/ipython
282 # on Linux. We have decided to go back to using .ipython everywhere
282 # on Linux. We have decided to go back to using .ipython everywhere
283 xdg_ipdir = pjoin(xdg_dir, 'ipython')
283 xdg_ipdir = pjoin(xdg_dir, 'ipython')
284
284
285 if _writable_dir(xdg_ipdir):
285 if _writable_dir(xdg_ipdir):
286 cu = compress_user
286 cu = compress_user
287 if os.path.exists(ipdir):
287 if os.path.exists(ipdir):
288 warn(('Ignoring {0} in favour of {1}. Remove {0} to '
288 warn(('Ignoring {0} in favour of {1}. Remove {0} to '
289 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
289 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
290 elif os.path.islink(xdg_ipdir):
290 elif os.path.islink(xdg_ipdir):
291 warn(('{0} is deprecated. Move link to {1} to '
291 warn(('{0} is deprecated. Move link to {1} to '
292 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
292 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
293 else:
293 else:
294 warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
294 warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
295 shutil.move(xdg_ipdir, ipdir)
295 shutil.move(xdg_ipdir, ipdir)
296
296
297 ipdir = os.path.normpath(os.path.expanduser(ipdir))
297 ipdir = os.path.normpath(os.path.expanduser(ipdir))
298
298
299 if os.path.exists(ipdir) and not _writable_dir(ipdir):
299 if os.path.exists(ipdir) and not _writable_dir(ipdir):
300 # ipdir exists, but is not writable
300 # ipdir exists, but is not writable
301 warn("IPython dir '{0}' is not a writable location,"
301 warn("IPython dir '{0}' is not a writable location,"
302 " using a temp directory.".format(ipdir))
302 " using a temp directory.".format(ipdir))
303 ipdir = tempfile.mkdtemp()
303 ipdir = tempfile.mkdtemp()
304 elif not os.path.exists(ipdir):
304 elif not os.path.exists(ipdir):
305 parent = os.path.dirname(ipdir)
305 parent = os.path.dirname(ipdir)
306 if not _writable_dir(parent):
306 if not _writable_dir(parent):
307 # ipdir does not exist and parent isn't writable
307 # ipdir does not exist and parent isn't writable
308 warn("IPython parent '{0}' is not a writable location,"
308 warn("IPython parent '{0}' is not a writable location,"
309 " using a temp directory.".format(parent))
309 " using a temp directory.".format(parent))
310 ipdir = tempfile.mkdtemp()
310 ipdir = tempfile.mkdtemp()
311
311
312 return py3compat.cast_unicode(ipdir, fs_encoding)
312 return py3compat.cast_unicode(ipdir, fs_encoding)
313
313
314
314
315 def get_ipython_cache_dir():
315 def get_ipython_cache_dir():
316 """Get the cache directory it is created if it does not exist."""
316 """Get the cache directory it is created if it does not exist."""
317 xdgdir = get_xdg_cache_dir()
317 xdgdir = get_xdg_cache_dir()
318 if xdgdir is None:
318 if xdgdir is None:
319 return get_ipython_dir()
319 return get_ipython_dir()
320 ipdir = os.path.join(xdgdir, "ipython")
320 ipdir = os.path.join(xdgdir, "ipython")
321 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
321 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
322 ensure_dir_exists(ipdir)
322 ensure_dir_exists(ipdir)
323 elif not _writable_dir(xdgdir):
323 elif not _writable_dir(xdgdir):
324 return get_ipython_dir()
324 return get_ipython_dir()
325
325
326 return py3compat.cast_unicode(ipdir, fs_encoding)
326 return py3compat.cast_unicode(ipdir, fs_encoding)
327
327
328
328
329 def get_ipython_package_dir():
329 def get_ipython_package_dir():
330 """Get the base directory where IPython itself is installed."""
330 """Get the base directory where IPython itself is installed."""
331 ipdir = os.path.dirname(IPython.__file__)
331 ipdir = os.path.dirname(IPython.__file__)
332 return py3compat.cast_unicode(ipdir, fs_encoding)
332 return py3compat.cast_unicode(ipdir, fs_encoding)
333
333
334
334
335 def get_ipython_module_path(module_str):
335 def get_ipython_module_path(module_str):
336 """Find the path to an IPython module in this version of IPython.
336 """Find the path to an IPython module in this version of IPython.
337
337
338 This will always find the version of the module that is in this importable
338 This will always find the version of the module that is in this importable
339 IPython package. This will always return the path to the ``.py``
339 IPython package. This will always return the path to the ``.py``
340 version of the module.
340 version of the module.
341 """
341 """
342 if module_str == 'IPython':
342 if module_str == 'IPython':
343 return os.path.join(get_ipython_package_dir(), '__init__.py')
343 return os.path.join(get_ipython_package_dir(), '__init__.py')
344 mod = import_item(module_str)
344 mod = import_item(module_str)
345 the_path = mod.__file__.replace('.pyc', '.py')
345 the_path = mod.__file__.replace('.pyc', '.py')
346 the_path = the_path.replace('.pyo', '.py')
346 the_path = the_path.replace('.pyo', '.py')
347 return py3compat.cast_unicode(the_path, fs_encoding)
347 return py3compat.cast_unicode(the_path, fs_encoding)
348
348
349 def locate_profile(profile='default'):
349 def locate_profile(profile='default'):
350 """Find the path to the folder associated with a given profile.
350 """Find the path to the folder associated with a given profile.
351
351
352 I.e. find $IPYTHONDIR/profile_whatever.
352 I.e. find $IPYTHONDIR/profile_whatever.
353 """
353 """
354 from IPython.core.profiledir import ProfileDir, ProfileDirError
354 from IPython.core.profiledir import ProfileDir, ProfileDirError
355 try:
355 try:
356 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
356 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
357 except ProfileDirError:
357 except ProfileDirError:
358 # IOError makes more sense when people are expecting a path
358 # IOError makes more sense when people are expecting a path
359 raise IOError("Couldn't find profile %r" % profile)
359 raise IOError("Couldn't find profile %r" % profile)
360 return pd.location
360 return pd.location
361
361
362 def expand_path(s):
362 def expand_path(s):
363 """Expand $VARS and ~names in a string, like a shell
363 """Expand $VARS and ~names in a string, like a shell
364
364
365 :Examples:
365 :Examples:
366
366
367 In [2]: os.environ['FOO']='test'
367 In [2]: os.environ['FOO']='test'
368
368
369 In [3]: expand_path('variable FOO is $FOO')
369 In [3]: expand_path('variable FOO is $FOO')
370 Out[3]: 'variable FOO is test'
370 Out[3]: 'variable FOO is test'
371 """
371 """
372 # This is a pretty subtle hack. When expand user is given a UNC path
372 # This is a pretty subtle hack. When expand user is given a UNC path
373 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
373 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
374 # the $ to get (\\server\share\%username%). I think it considered $
374 # the $ to get (\\server\share\%username%). I think it considered $
375 # alone an empty var. But, we need the $ to remains there (it indicates
375 # alone an empty var. But, we need the $ to remains there (it indicates
376 # a hidden share).
376 # a hidden share).
377 if os.name=='nt':
377 if os.name=='nt':
378 s = s.replace('$\\', 'IPYTHON_TEMP')
378 s = s.replace('$\\', 'IPYTHON_TEMP')
379 s = os.path.expandvars(os.path.expanduser(s))
379 s = os.path.expandvars(os.path.expanduser(s))
380 if os.name=='nt':
380 if os.name=='nt':
381 s = s.replace('IPYTHON_TEMP', '$\\')
381 s = s.replace('IPYTHON_TEMP', '$\\')
382 return s
382 return s
383
383
384
384
385 def unescape_glob(string):
385 def unescape_glob(string):
386 """Unescape glob pattern in `string`."""
386 """Unescape glob pattern in `string`."""
387 def unescape(s):
387 def unescape(s):
388 for pattern in '*[]!?':
388 for pattern in '*[]!?':
389 s = s.replace(r'\{0}'.format(pattern), pattern)
389 s = s.replace(r'\{0}'.format(pattern), pattern)
390 return s
390 return s
391 return '\\'.join(map(unescape, string.split('\\\\')))
391 return '\\'.join(map(unescape, string.split('\\\\')))
392
392
393
393
394 def shellglob(args):
394 def shellglob(args):
395 """
395 """
396 Do glob expansion for each element in `args` and return a flattened list.
396 Do glob expansion for each element in `args` and return a flattened list.
397
397
398 Unmatched glob pattern will remain as-is in the returned list.
398 Unmatched glob pattern will remain as-is in the returned list.
399
399
400 """
400 """
401 expanded = []
401 expanded = []
402 # Do not unescape backslash in Windows as it is interpreted as
402 # Do not unescape backslash in Windows as it is interpreted as
403 # path separator:
403 # path separator:
404 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
404 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
405 for a in args:
405 for a in args:
406 expanded.extend(glob.glob(a) or [unescape(a)])
406 expanded.extend(glob.glob(a) or [unescape(a)])
407 return expanded
407 return expanded
408
408
409
409
410 def target_outdated(target,deps):
410 def target_outdated(target,deps):
411 """Determine whether a target is out of date.
411 """Determine whether a target is out of date.
412
412
413 target_outdated(target,deps) -> 1/0
413 target_outdated(target,deps) -> 1/0
414
414
415 deps: list of filenames which MUST exist.
415 deps: list of filenames which MUST exist.
416 target: single filename which may or may not exist.
416 target: single filename which may or may not exist.
417
417
418 If target doesn't exist or is older than any file listed in deps, return
418 If target doesn't exist or is older than any file listed in deps, return
419 true, otherwise return false.
419 true, otherwise return false.
420 """
420 """
421 try:
421 try:
422 target_time = os.path.getmtime(target)
422 target_time = os.path.getmtime(target)
423 except os.error:
423 except os.error:
424 return 1
424 return 1
425 for dep in deps:
425 for dep in deps:
426 dep_time = os.path.getmtime(dep)
426 dep_time = os.path.getmtime(dep)
427 if dep_time > target_time:
427 if dep_time > target_time:
428 #print "For target",target,"Dep failed:",dep # dbg
428 #print "For target",target,"Dep failed:",dep # dbg
429 #print "times (dep,tar):",dep_time,target_time # dbg
429 #print "times (dep,tar):",dep_time,target_time # dbg
430 return 1
430 return 1
431 return 0
431 return 0
432
432
433
433
434 def target_update(target,deps,cmd):
434 def target_update(target,deps,cmd):
435 """Update a target with a given command given a list of dependencies.
435 """Update a target with a given command given a list of dependencies.
436
436
437 target_update(target,deps,cmd) -> runs cmd if target is outdated.
437 target_update(target,deps,cmd) -> runs cmd if target is outdated.
438
438
439 This is just a wrapper around target_outdated() which calls the given
439 This is just a wrapper around target_outdated() which calls the given
440 command if target is outdated."""
440 command if target is outdated."""
441
441
442 if target_outdated(target,deps):
442 if target_outdated(target,deps):
443 system(cmd)
443 system(cmd)
444
444
445 def filehash(path):
445 def filehash(path):
446 """Make an MD5 hash of a file, ignoring any differences in line
446 """Make an MD5 hash of a file, ignoring any differences in line
447 ending characters."""
447 ending characters."""
448 with open(path, "rU") as f:
448 with open(path, "rU") as f:
449 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
449 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
450
450
451 # If the config is unmodified from the default, we'll just delete it.
451 # If the config is unmodified from the default, we'll just delete it.
452 # These are consistent for 0.10.x, thankfully. We're not going to worry about
452 # These are consistent for 0.10.x, thankfully. We're not going to worry about
453 # older versions.
453 # older versions.
454 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
454 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
455 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
455 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
456
456
457 def check_for_old_config(ipython_dir=None):
457 def check_for_old_config(ipython_dir=None):
458 """Check for old config files, and present a warning if they exist.
458 """Check for old config files, and present a warning if they exist.
459
459
460 A link to the docs of the new config is included in the message.
460 A link to the docs of the new config is included in the message.
461
461
462 This should mitigate confusion with the transition to the new
462 This should mitigate confusion with the transition to the new
463 config system in 0.11.
463 config system in 0.11.
464 """
464 """
465 if ipython_dir is None:
465 if ipython_dir is None:
466 ipython_dir = get_ipython_dir()
466 ipython_dir = get_ipython_dir()
467
467
468 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
468 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
469 warned = False
469 warned = False
470 for cfg in old_configs:
470 for cfg in old_configs:
471 f = os.path.join(ipython_dir, cfg)
471 f = os.path.join(ipython_dir, cfg)
472 if os.path.exists(f):
472 if os.path.exists(f):
473 if filehash(f) == old_config_md5.get(cfg, ''):
473 if filehash(f) == old_config_md5.get(cfg, ''):
474 os.unlink(f)
474 os.unlink(f)
475 else:
475 else:
476 warn("Found old IPython config file {0} (modified by user)".format(f))
476 warn("Found old IPython config file {!r} (modified by user)".format(f))
477 warned = True
477 warned = True
478
478
479 if warned:
479 if warned:
480 warn("""
480 warn("""
481 The IPython configuration system has changed as of 0.11, and these files will
481 The IPython configuration system has changed as of 0.11, and these files will
482 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
482 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
483 of the new config system.
483 of the new config system.
484 To start configuring IPython, do `ipython profile create`, and edit
484 To start configuring IPython, do `ipython profile create`, and edit
485 `ipython_config.py` in <ipython_dir>/profile_default.
485 `ipython_config.py` in <ipython_dir>/profile_default.
486 If you need to leave the old config files in place for an older version of
486 If you need to leave the old config files in place for an older version of
487 IPython and want to suppress this warning message, set
487 IPython and want to suppress this warning message, set
488 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
488 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
489
489
490 def get_security_file(filename, profile='default'):
490 def get_security_file(filename, profile='default'):
491 """Return the absolute path of a security file given by filename and profile
491 """Return the absolute path of a security file given by filename and profile
492
492
493 This allows users and developers to find security files without
493 This allows users and developers to find security files without
494 knowledge of the IPython directory structure. The search path
494 knowledge of the IPython directory structure. The search path
495 will be ['.', profile.security_dir]
495 will be ['.', profile.security_dir]
496
496
497 Parameters
497 Parameters
498 ----------
498 ----------
499
499
500 filename : str
500 filename : str
501 The file to be found. If it is passed as an absolute path, it will
501 The file to be found. If it is passed as an absolute path, it will
502 simply be returned.
502 simply be returned.
503 profile : str [default: 'default']
503 profile : str [default: 'default']
504 The name of the profile to search. Leaving this unspecified
504 The name of the profile to search. Leaving this unspecified
505 The file to be found. If it is passed as an absolute path, fname will
505 The file to be found. If it is passed as an absolute path, fname will
506 simply be returned.
506 simply be returned.
507
507
508 Returns
508 Returns
509 -------
509 -------
510 Raises :exc:`IOError` if file not found or returns absolute path to file.
510 Raises :exc:`IOError` if file not found or returns absolute path to file.
511 """
511 """
512 # import here, because profiledir also imports from utils.path
512 # import here, because profiledir also imports from utils.path
513 from IPython.core.profiledir import ProfileDir
513 from IPython.core.profiledir import ProfileDir
514 try:
514 try:
515 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
515 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
516 except Exception:
516 except Exception:
517 # will raise ProfileDirError if no such profile
517 # will raise ProfileDirError if no such profile
518 raise IOError("Profile %r not found")
518 raise IOError("Profile %r not found")
519 return filefind(filename, ['.', pd.security_dir])
519 return filefind(filename, ['.', pd.security_dir])
520
520
521
521
522 ENOLINK = 1998
522 ENOLINK = 1998
523
523
524 def link(src, dst):
524 def link(src, dst):
525 """Hard links ``src`` to ``dst``, returning 0 or errno.
525 """Hard links ``src`` to ``dst``, returning 0 or errno.
526
526
527 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
527 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
528 supported by the operating system.
528 supported by the operating system.
529 """
529 """
530
530
531 if not hasattr(os, "link"):
531 if not hasattr(os, "link"):
532 return ENOLINK
532 return ENOLINK
533 link_errno = 0
533 link_errno = 0
534 try:
534 try:
535 os.link(src, dst)
535 os.link(src, dst)
536 except OSError as e:
536 except OSError as e:
537 link_errno = e.errno
537 link_errno = e.errno
538 return link_errno
538 return link_errno
539
539
540
540
541 def link_or_copy(src, dst):
541 def link_or_copy(src, dst):
542 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
542 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
543
543
544 Attempts to maintain the semantics of ``shutil.copy``.
544 Attempts to maintain the semantics of ``shutil.copy``.
545
545
546 Because ``os.link`` does not overwrite files, a unique temporary file
546 Because ``os.link`` does not overwrite files, a unique temporary file
547 will be used if the target already exists, then that file will be moved
547 will be used if the target already exists, then that file will be moved
548 into place.
548 into place.
549 """
549 """
550
550
551 if os.path.isdir(dst):
551 if os.path.isdir(dst):
552 dst = os.path.join(dst, os.path.basename(src))
552 dst = os.path.join(dst, os.path.basename(src))
553
553
554 link_errno = link(src, dst)
554 link_errno = link(src, dst)
555 if link_errno == errno.EEXIST:
555 if link_errno == errno.EEXIST:
556 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
556 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
557 try:
557 try:
558 link_or_copy(src, new_dst)
558 link_or_copy(src, new_dst)
559 except:
559 except:
560 try:
560 try:
561 os.remove(new_dst)
561 os.remove(new_dst)
562 except OSError:
562 except OSError:
563 pass
563 pass
564 raise
564 raise
565 os.rename(new_dst, dst)
565 os.rename(new_dst, dst)
566 elif link_errno != 0:
566 elif link_errno != 0:
567 # Either link isn't supported, or the filesystem doesn't support
567 # Either link isn't supported, or the filesystem doesn't support
568 # linking, or 'src' and 'dst' are on different filesystems.
568 # linking, or 'src' and 'dst' are on different filesystems.
569 shutil.copy(src, dst)
569 shutil.copy(src, dst)
570
570
571 def ensure_dir_exists(path, mode=0o755):
571 def ensure_dir_exists(path, mode=0o755):
572 """ensure that a directory exists
572 """ensure that a directory exists
573
573
574 If it doesn't exist, try to create it and protect against a race condition
574 If it doesn't exist, try to create it and protect against a race condition
575 if another process is doing the same.
575 if another process is doing the same.
576
576
577 The default permissions are 755, which differ from os.makedirs default of 777.
577 The default permissions are 755, which differ from os.makedirs default of 777.
578 """
578 """
579 if not os.path.exists(path):
579 if not os.path.exists(path):
580 try:
580 try:
581 os.makedirs(path, mode=mode)
581 os.makedirs(path, mode=mode)
582 except OSError as e:
582 except OSError as e:
583 if e.errno != errno.EEXIST:
583 if e.errno != errno.EEXIST:
584 raise
584 raise
585 elif not os.path.isdir(path):
585 elif not os.path.isdir(path):
586 raise IOError("%r exists but is not a directory" % path)
586 raise IOError("%r exists but is not a directory" % path)
General Comments 0
You need to be logged in to leave comments. Login now