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