##// END OF EJS Templates
Don't link file again if it's already a correct hard link...
Thomas Kluyver -
Show More
@@ -1,587 +1,593 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 _writable_dir(path):
30 def _writable_dir(path):
31 """Whether `path` is a directory, to which the user has write access."""
31 """Whether `path` is a directory, to which the user has write access."""
32 return os.path.isdir(path) and os.access(path, os.W_OK)
32 return os.path.isdir(path) and os.access(path, os.W_OK)
33
33
34 if sys.platform == 'win32':
34 if sys.platform == 'win32':
35 @skip_doctest
35 @skip_doctest
36 def _get_long_path_name(path):
36 def _get_long_path_name(path):
37 """Get a long path name (expand ~) on Windows using ctypes.
37 """Get a long path name (expand ~) on Windows using ctypes.
38
38
39 Examples
39 Examples
40 --------
40 --------
41
41
42 >>> get_long_path_name('c:\\docume~1')
42 >>> get_long_path_name('c:\\docume~1')
43 u'c:\\\\Documents and Settings'
43 u'c:\\\\Documents and Settings'
44
44
45 """
45 """
46 try:
46 try:
47 import ctypes
47 import ctypes
48 except ImportError:
48 except ImportError:
49 raise ImportError('you need to have ctypes installed for this to work')
49 raise ImportError('you need to have ctypes installed for this to work')
50 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
50 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
51 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
51 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
52 ctypes.c_uint ]
52 ctypes.c_uint ]
53
53
54 buf = ctypes.create_unicode_buffer(260)
54 buf = ctypes.create_unicode_buffer(260)
55 rv = _GetLongPathName(path, buf, 260)
55 rv = _GetLongPathName(path, buf, 260)
56 if rv == 0 or rv > 260:
56 if rv == 0 or rv > 260:
57 return path
57 return path
58 else:
58 else:
59 return buf.value
59 return buf.value
60 else:
60 else:
61 def _get_long_path_name(path):
61 def _get_long_path_name(path):
62 """Dummy no-op."""
62 """Dummy no-op."""
63 return path
63 return path
64
64
65
65
66
66
67 def get_long_path_name(path):
67 def get_long_path_name(path):
68 """Expand a path into its long form.
68 """Expand a path into its long form.
69
69
70 On Windows this expands any ~ in the paths. On other platforms, it is
70 On Windows this expands any ~ in the paths. On other platforms, it is
71 a null operation.
71 a null operation.
72 """
72 """
73 return _get_long_path_name(path)
73 return _get_long_path_name(path)
74
74
75
75
76 def unquote_filename(name, win32=(sys.platform=='win32')):
76 def unquote_filename(name, win32=(sys.platform=='win32')):
77 """ On Windows, remove leading and trailing quotes from filenames.
77 """ On Windows, remove leading and trailing quotes from filenames.
78 """
78 """
79 if win32:
79 if win32:
80 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
80 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
81 name = name[1:-1]
81 name = name[1:-1]
82 return name
82 return name
83
83
84 def compress_user(path):
84 def compress_user(path):
85 """Reverse of :func:`os.path.expanduser`
85 """Reverse of :func:`os.path.expanduser`
86 """
86 """
87 home = os.path.expanduser('~')
87 home = os.path.expanduser('~')
88 if path.startswith(home):
88 if path.startswith(home):
89 path = "~" + path[len(home):]
89 path = "~" + path[len(home):]
90 return path
90 return path
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, py3compat.string_types):
158 elif isinstance(path_dirs, py3compat.string_types):
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 = py3compat.getcwd()
162 if path == '.': path = py3compat.getcwd()
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 try:
202 try:
203 import winreg as wreg # Py 3
203 import winreg as wreg # Py 3
204 except ImportError:
204 except ImportError:
205 import _winreg as wreg # Py 2
205 import _winreg as wreg # Py 2
206 key = wreg.OpenKey(
206 key = wreg.OpenKey(
207 wreg.HKEY_CURRENT_USER,
207 wreg.HKEY_CURRENT_USER,
208 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
208 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
209 )
209 )
210 homedir = wreg.QueryValueEx(key,'Personal')[0]
210 homedir = wreg.QueryValueEx(key,'Personal')[0]
211 key.Close()
211 key.Close()
212 except:
212 except:
213 pass
213 pass
214
214
215 if (not require_writable) or _writable_dir(homedir):
215 if (not require_writable) or _writable_dir(homedir):
216 return py3compat.cast_unicode(homedir, fs_encoding)
216 return py3compat.cast_unicode(homedir, fs_encoding)
217 else:
217 else:
218 raise HomeDirError('%s is not a writable dir, '
218 raise HomeDirError('%s is not a writable dir, '
219 'set $HOME environment variable to override' % homedir)
219 'set $HOME environment variable to override' % homedir)
220
220
221 def get_xdg_dir():
221 def get_xdg_dir():
222 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
222 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
223
223
224 This is only for non-OS X posix (Linux,Unix,etc.) systems.
224 This is only for non-OS X posix (Linux,Unix,etc.) systems.
225 """
225 """
226
226
227 env = os.environ
227 env = os.environ
228
228
229 if os.name == 'posix' and sys.platform != 'darwin':
229 if os.name == 'posix' and sys.platform != 'darwin':
230 # Linux, Unix, AIX, etc.
230 # Linux, Unix, AIX, etc.
231 # use ~/.config if empty OR not set
231 # use ~/.config if empty OR not set
232 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
232 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
233 if xdg and _writable_dir(xdg):
233 if xdg and _writable_dir(xdg):
234 return py3compat.cast_unicode(xdg, fs_encoding)
234 return py3compat.cast_unicode(xdg, fs_encoding)
235
235
236 return None
236 return None
237
237
238
238
239 def get_xdg_cache_dir():
239 def get_xdg_cache_dir():
240 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
240 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
241
241
242 This is only for non-OS X posix (Linux,Unix,etc.) systems.
242 This is only for non-OS X posix (Linux,Unix,etc.) systems.
243 """
243 """
244
244
245 env = os.environ
245 env = os.environ
246
246
247 if os.name == 'posix' and sys.platform != 'darwin':
247 if os.name == 'posix' and sys.platform != 'darwin':
248 # Linux, Unix, AIX, etc.
248 # Linux, Unix, AIX, etc.
249 # use ~/.cache if empty OR not set
249 # use ~/.cache if empty OR not set
250 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
250 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
251 if xdg and _writable_dir(xdg):
251 if xdg and _writable_dir(xdg):
252 return py3compat.cast_unicode(xdg, fs_encoding)
252 return py3compat.cast_unicode(xdg, fs_encoding)
253
253
254 return None
254 return None
255
255
256
256
257 def get_ipython_dir():
257 def get_ipython_dir():
258 """Get the IPython directory for this platform and user.
258 """Get the IPython directory for this platform and user.
259
259
260 This uses the logic in `get_home_dir` to find the home directory
260 This uses the logic in `get_home_dir` to find the home directory
261 and then adds .ipython to the end of the path.
261 and then adds .ipython to the end of the path.
262 """
262 """
263
263
264 env = os.environ
264 env = os.environ
265 pjoin = os.path.join
265 pjoin = os.path.join
266
266
267
267
268 ipdir_def = '.ipython'
268 ipdir_def = '.ipython'
269
269
270 home_dir = get_home_dir()
270 home_dir = get_home_dir()
271 xdg_dir = get_xdg_dir()
271 xdg_dir = get_xdg_dir()
272
272
273 # import pdb; pdb.set_trace() # dbg
273 # import pdb; pdb.set_trace() # dbg
274 if 'IPYTHON_DIR' in env:
274 if 'IPYTHON_DIR' in env:
275 warn('The environment variable IPYTHON_DIR is deprecated. '
275 warn('The environment variable IPYTHON_DIR is deprecated. '
276 'Please use IPYTHONDIR instead.')
276 'Please use IPYTHONDIR instead.')
277 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
277 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
278 if ipdir is None:
278 if ipdir is None:
279 # not set explicitly, use ~/.ipython
279 # not set explicitly, use ~/.ipython
280 ipdir = pjoin(home_dir, ipdir_def)
280 ipdir = pjoin(home_dir, ipdir_def)
281 if xdg_dir:
281 if xdg_dir:
282 # Several IPython versions (up to 1.x) defaulted to .config/ipython
282 # Several IPython versions (up to 1.x) defaulted to .config/ipython
283 # on Linux. We have decided to go back to using .ipython everywhere
283 # on Linux. We have decided to go back to using .ipython everywhere
284 xdg_ipdir = pjoin(xdg_dir, 'ipython')
284 xdg_ipdir = pjoin(xdg_dir, 'ipython')
285
285
286 if _writable_dir(xdg_ipdir):
286 if _writable_dir(xdg_ipdir):
287 cu = compress_user
287 cu = compress_user
288 if os.path.exists(ipdir):
288 if os.path.exists(ipdir):
289 warn(('Ignoring {0} in favour of {1}. Remove {0} to '
289 warn(('Ignoring {0} in favour of {1}. Remove {0} to '
290 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
290 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
291 elif os.path.islink(xdg_ipdir):
291 elif os.path.islink(xdg_ipdir):
292 warn(('{0} is deprecated. Move link to {1} to '
292 warn(('{0} is deprecated. Move link to {1} to '
293 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
293 'get rid of this message').format(cu(xdg_ipdir), cu(ipdir)))
294 else:
294 else:
295 warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
295 warn('Moving {0} to {1}'.format(cu(xdg_ipdir), cu(ipdir)))
296 shutil.move(xdg_ipdir, ipdir)
296 shutil.move(xdg_ipdir, ipdir)
297
297
298 ipdir = os.path.normpath(os.path.expanduser(ipdir))
298 ipdir = os.path.normpath(os.path.expanduser(ipdir))
299
299
300 if os.path.exists(ipdir) and not _writable_dir(ipdir):
300 if os.path.exists(ipdir) and not _writable_dir(ipdir):
301 # ipdir exists, but is not writable
301 # ipdir exists, but is not writable
302 warn("IPython dir '{0}' is not a writable location,"
302 warn("IPython dir '{0}' is not a writable location,"
303 " using a temp directory.".format(ipdir))
303 " using a temp directory.".format(ipdir))
304 ipdir = tempfile.mkdtemp()
304 ipdir = tempfile.mkdtemp()
305 elif not os.path.exists(ipdir):
305 elif not os.path.exists(ipdir):
306 parent = os.path.dirname(ipdir)
306 parent = os.path.dirname(ipdir)
307 if not _writable_dir(parent):
307 if not _writable_dir(parent):
308 # ipdir does not exist and parent isn't writable
308 # ipdir does not exist and parent isn't writable
309 warn("IPython parent '{0}' is not a writable location,"
309 warn("IPython parent '{0}' is not a writable location,"
310 " using a temp directory.".format(parent))
310 " using a temp directory.".format(parent))
311 ipdir = tempfile.mkdtemp()
311 ipdir = tempfile.mkdtemp()
312
312
313 return py3compat.cast_unicode(ipdir, fs_encoding)
313 return py3compat.cast_unicode(ipdir, fs_encoding)
314
314
315
315
316 def get_ipython_cache_dir():
316 def get_ipython_cache_dir():
317 """Get the cache directory it is created if it does not exist."""
317 """Get the cache directory it is created if it does not exist."""
318 xdgdir = get_xdg_cache_dir()
318 xdgdir = get_xdg_cache_dir()
319 if xdgdir is None:
319 if xdgdir is None:
320 return get_ipython_dir()
320 return get_ipython_dir()
321 ipdir = os.path.join(xdgdir, "ipython")
321 ipdir = os.path.join(xdgdir, "ipython")
322 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
322 if not os.path.exists(ipdir) and _writable_dir(xdgdir):
323 ensure_dir_exists(ipdir)
323 ensure_dir_exists(ipdir)
324 elif not _writable_dir(xdgdir):
324 elif not _writable_dir(xdgdir):
325 return get_ipython_dir()
325 return get_ipython_dir()
326
326
327 return py3compat.cast_unicode(ipdir, fs_encoding)
327 return py3compat.cast_unicode(ipdir, fs_encoding)
328
328
329
329
330 def get_ipython_package_dir():
330 def get_ipython_package_dir():
331 """Get the base directory where IPython itself is installed."""
331 """Get the base directory where IPython itself is installed."""
332 ipdir = os.path.dirname(IPython.__file__)
332 ipdir = os.path.dirname(IPython.__file__)
333 return py3compat.cast_unicode(ipdir, fs_encoding)
333 return py3compat.cast_unicode(ipdir, fs_encoding)
334
334
335
335
336 def get_ipython_module_path(module_str):
336 def get_ipython_module_path(module_str):
337 """Find the path to an IPython module in this version of IPython.
337 """Find the path to an IPython module in this version of IPython.
338
338
339 This will always find the version of the module that is in this importable
339 This will always find the version of the module that is in this importable
340 IPython package. This will always return the path to the ``.py``
340 IPython package. This will always return the path to the ``.py``
341 version of the module.
341 version of the module.
342 """
342 """
343 if module_str == 'IPython':
343 if module_str == 'IPython':
344 return os.path.join(get_ipython_package_dir(), '__init__.py')
344 return os.path.join(get_ipython_package_dir(), '__init__.py')
345 mod = import_item(module_str)
345 mod = import_item(module_str)
346 the_path = mod.__file__.replace('.pyc', '.py')
346 the_path = mod.__file__.replace('.pyc', '.py')
347 the_path = the_path.replace('.pyo', '.py')
347 the_path = the_path.replace('.pyo', '.py')
348 return py3compat.cast_unicode(the_path, fs_encoding)
348 return py3compat.cast_unicode(the_path, fs_encoding)
349
349
350 def locate_profile(profile='default'):
350 def locate_profile(profile='default'):
351 """Find the path to the folder associated with a given profile.
351 """Find the path to the folder associated with a given profile.
352
352
353 I.e. find $IPYTHONDIR/profile_whatever.
353 I.e. find $IPYTHONDIR/profile_whatever.
354 """
354 """
355 from IPython.core.profiledir import ProfileDir, ProfileDirError
355 from IPython.core.profiledir import ProfileDir, ProfileDirError
356 try:
356 try:
357 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
357 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
358 except ProfileDirError:
358 except ProfileDirError:
359 # IOError makes more sense when people are expecting a path
359 # IOError makes more sense when people are expecting a path
360 raise IOError("Couldn't find profile %r" % profile)
360 raise IOError("Couldn't find profile %r" % profile)
361 return pd.location
361 return pd.location
362
362
363 def expand_path(s):
363 def expand_path(s):
364 """Expand $VARS and ~names in a string, like a shell
364 """Expand $VARS and ~names in a string, like a shell
365
365
366 :Examples:
366 :Examples:
367
367
368 In [2]: os.environ['FOO']='test'
368 In [2]: os.environ['FOO']='test'
369
369
370 In [3]: expand_path('variable FOO is $FOO')
370 In [3]: expand_path('variable FOO is $FOO')
371 Out[3]: 'variable FOO is test'
371 Out[3]: 'variable FOO is test'
372 """
372 """
373 # This is a pretty subtle hack. When expand user is given a UNC path
373 # This is a pretty subtle hack. When expand user is given a UNC path
374 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
374 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
375 # the $ to get (\\server\share\%username%). I think it considered $
375 # the $ to get (\\server\share\%username%). I think it considered $
376 # alone an empty var. But, we need the $ to remains there (it indicates
376 # alone an empty var. But, we need the $ to remains there (it indicates
377 # a hidden share).
377 # a hidden share).
378 if os.name=='nt':
378 if os.name=='nt':
379 s = s.replace('$\\', 'IPYTHON_TEMP')
379 s = s.replace('$\\', 'IPYTHON_TEMP')
380 s = os.path.expandvars(os.path.expanduser(s))
380 s = os.path.expandvars(os.path.expanduser(s))
381 if os.name=='nt':
381 if os.name=='nt':
382 s = s.replace('IPYTHON_TEMP', '$\\')
382 s = s.replace('IPYTHON_TEMP', '$\\')
383 return s
383 return s
384
384
385
385
386 def unescape_glob(string):
386 def unescape_glob(string):
387 """Unescape glob pattern in `string`."""
387 """Unescape glob pattern in `string`."""
388 def unescape(s):
388 def unescape(s):
389 for pattern in '*[]!?':
389 for pattern in '*[]!?':
390 s = s.replace(r'\{0}'.format(pattern), pattern)
390 s = s.replace(r'\{0}'.format(pattern), pattern)
391 return s
391 return s
392 return '\\'.join(map(unescape, string.split('\\\\')))
392 return '\\'.join(map(unescape, string.split('\\\\')))
393
393
394
394
395 def shellglob(args):
395 def shellglob(args):
396 """
396 """
397 Do glob expansion for each element in `args` and return a flattened list.
397 Do glob expansion for each element in `args` and return a flattened list.
398
398
399 Unmatched glob pattern will remain as-is in the returned list.
399 Unmatched glob pattern will remain as-is in the returned list.
400
400
401 """
401 """
402 expanded = []
402 expanded = []
403 # Do not unescape backslash in Windows as it is interpreted as
403 # Do not unescape backslash in Windows as it is interpreted as
404 # path separator:
404 # path separator:
405 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
405 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
406 for a in args:
406 for a in args:
407 expanded.extend(glob.glob(a) or [unescape(a)])
407 expanded.extend(glob.glob(a) or [unescape(a)])
408 return expanded
408 return expanded
409
409
410
410
411 def target_outdated(target,deps):
411 def target_outdated(target,deps):
412 """Determine whether a target is out of date.
412 """Determine whether a target is out of date.
413
413
414 target_outdated(target,deps) -> 1/0
414 target_outdated(target,deps) -> 1/0
415
415
416 deps: list of filenames which MUST exist.
416 deps: list of filenames which MUST exist.
417 target: single filename which may or may not exist.
417 target: single filename which may or may not exist.
418
418
419 If target doesn't exist or is older than any file listed in deps, return
419 If target doesn't exist or is older than any file listed in deps, return
420 true, otherwise return false.
420 true, otherwise return false.
421 """
421 """
422 try:
422 try:
423 target_time = os.path.getmtime(target)
423 target_time = os.path.getmtime(target)
424 except os.error:
424 except os.error:
425 return 1
425 return 1
426 for dep in deps:
426 for dep in deps:
427 dep_time = os.path.getmtime(dep)
427 dep_time = os.path.getmtime(dep)
428 if dep_time > target_time:
428 if dep_time > target_time:
429 #print "For target",target,"Dep failed:",dep # dbg
429 #print "For target",target,"Dep failed:",dep # dbg
430 #print "times (dep,tar):",dep_time,target_time # dbg
430 #print "times (dep,tar):",dep_time,target_time # dbg
431 return 1
431 return 1
432 return 0
432 return 0
433
433
434
434
435 def target_update(target,deps,cmd):
435 def target_update(target,deps,cmd):
436 """Update a target with a given command given a list of dependencies.
436 """Update a target with a given command given a list of dependencies.
437
437
438 target_update(target,deps,cmd) -> runs cmd if target is outdated.
438 target_update(target,deps,cmd) -> runs cmd if target is outdated.
439
439
440 This is just a wrapper around target_outdated() which calls the given
440 This is just a wrapper around target_outdated() which calls the given
441 command if target is outdated."""
441 command if target is outdated."""
442
442
443 if target_outdated(target,deps):
443 if target_outdated(target,deps):
444 system(cmd)
444 system(cmd)
445
445
446 def filehash(path):
446 def filehash(path):
447 """Make an MD5 hash of a file, ignoring any differences in line
447 """Make an MD5 hash of a file, ignoring any differences in line
448 ending characters."""
448 ending characters."""
449 with open(path, "rU") as f:
449 with open(path, "rU") as f:
450 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
450 return md5(py3compat.str_to_bytes(f.read())).hexdigest()
451
451
452 # If the config is unmodified from the default, we'll just delete it.
452 # If the config is unmodified from the default, we'll just delete it.
453 # These are consistent for 0.10.x, thankfully. We're not going to worry about
453 # These are consistent for 0.10.x, thankfully. We're not going to worry about
454 # older versions.
454 # older versions.
455 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
455 old_config_md5 = {'ipy_user_conf.py': 'fc108bedff4b9a00f91fa0a5999140d3',
456 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
456 'ipythonrc': '12a68954f3403eea2eec09dc8fe5a9b5'}
457
457
458 def check_for_old_config(ipython_dir=None):
458 def check_for_old_config(ipython_dir=None):
459 """Check for old config files, and present a warning if they exist.
459 """Check for old config files, and present a warning if they exist.
460
460
461 A link to the docs of the new config is included in the message.
461 A link to the docs of the new config is included in the message.
462
462
463 This should mitigate confusion with the transition to the new
463 This should mitigate confusion with the transition to the new
464 config system in 0.11.
464 config system in 0.11.
465 """
465 """
466 if ipython_dir is None:
466 if ipython_dir is None:
467 ipython_dir = get_ipython_dir()
467 ipython_dir = get_ipython_dir()
468
468
469 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
469 old_configs = ['ipy_user_conf.py', 'ipythonrc', 'ipython_config.py']
470 warned = False
470 warned = False
471 for cfg in old_configs:
471 for cfg in old_configs:
472 f = os.path.join(ipython_dir, cfg)
472 f = os.path.join(ipython_dir, cfg)
473 if os.path.exists(f):
473 if os.path.exists(f):
474 if filehash(f) == old_config_md5.get(cfg, ''):
474 if filehash(f) == old_config_md5.get(cfg, ''):
475 os.unlink(f)
475 os.unlink(f)
476 else:
476 else:
477 warn("Found old IPython config file {!r} (modified by user)".format(f))
477 warn("Found old IPython config file {!r} (modified by user)".format(f))
478 warned = True
478 warned = True
479
479
480 if warned:
480 if warned:
481 warn("""
481 warn("""
482 The IPython configuration system has changed as of 0.11, and these files will
482 The IPython configuration system has changed as of 0.11, and these files will
483 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
483 be ignored. See http://ipython.github.com/ipython-doc/dev/config for details
484 of the new config system.
484 of the new config system.
485 To start configuring IPython, do `ipython profile create`, and edit
485 To start configuring IPython, do `ipython profile create`, and edit
486 `ipython_config.py` in <ipython_dir>/profile_default.
486 `ipython_config.py` in <ipython_dir>/profile_default.
487 If you need to leave the old config files in place for an older version of
487 If you need to leave the old config files in place for an older version of
488 IPython and want to suppress this warning message, set
488 IPython and want to suppress this warning message, set
489 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
489 `c.InteractiveShellApp.ignore_old_config=True` in the new config.""")
490
490
491 def get_security_file(filename, profile='default'):
491 def get_security_file(filename, profile='default'):
492 """Return the absolute path of a security file given by filename and profile
492 """Return the absolute path of a security file given by filename and profile
493
493
494 This allows users and developers to find security files without
494 This allows users and developers to find security files without
495 knowledge of the IPython directory structure. The search path
495 knowledge of the IPython directory structure. The search path
496 will be ['.', profile.security_dir]
496 will be ['.', profile.security_dir]
497
497
498 Parameters
498 Parameters
499 ----------
499 ----------
500
500
501 filename : str
501 filename : str
502 The file to be found. If it is passed as an absolute path, it will
502 The file to be found. If it is passed as an absolute path, it will
503 simply be returned.
503 simply be returned.
504 profile : str [default: 'default']
504 profile : str [default: 'default']
505 The name of the profile to search. Leaving this unspecified
505 The name of the profile to search. Leaving this unspecified
506 The file to be found. If it is passed as an absolute path, fname will
506 The file to be found. If it is passed as an absolute path, fname will
507 simply be returned.
507 simply be returned.
508
508
509 Returns
509 Returns
510 -------
510 -------
511 Raises :exc:`IOError` if file not found or returns absolute path to file.
511 Raises :exc:`IOError` if file not found or returns absolute path to file.
512 """
512 """
513 # import here, because profiledir also imports from utils.path
513 # import here, because profiledir also imports from utils.path
514 from IPython.core.profiledir import ProfileDir
514 from IPython.core.profiledir import ProfileDir
515 try:
515 try:
516 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
516 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), profile)
517 except Exception:
517 except Exception:
518 # will raise ProfileDirError if no such profile
518 # will raise ProfileDirError if no such profile
519 raise IOError("Profile %r not found")
519 raise IOError("Profile %r not found")
520 return filefind(filename, ['.', pd.security_dir])
520 return filefind(filename, ['.', pd.security_dir])
521
521
522
522
523 ENOLINK = 1998
523 ENOLINK = 1998
524
524
525 def link(src, dst):
525 def link(src, dst):
526 """Hard links ``src`` to ``dst``, returning 0 or errno.
526 """Hard links ``src`` to ``dst``, returning 0 or errno.
527
527
528 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
528 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
529 supported by the operating system.
529 supported by the operating system.
530 """
530 """
531
531
532 if not hasattr(os, "link"):
532 if not hasattr(os, "link"):
533 return ENOLINK
533 return ENOLINK
534 link_errno = 0
534 link_errno = 0
535 try:
535 try:
536 os.link(src, dst)
536 os.link(src, dst)
537 except OSError as e:
537 except OSError as e:
538 link_errno = e.errno
538 link_errno = e.errno
539 return link_errno
539 return link_errno
540
540
541
541
542 def link_or_copy(src, dst):
542 def link_or_copy(src, dst):
543 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
543 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
544
544
545 Attempts to maintain the semantics of ``shutil.copy``.
545 Attempts to maintain the semantics of ``shutil.copy``.
546
546
547 Because ``os.link`` does not overwrite files, a unique temporary file
547 Because ``os.link`` does not overwrite files, a unique temporary file
548 will be used if the target already exists, then that file will be moved
548 will be used if the target already exists, then that file will be moved
549 into place.
549 into place.
550 """
550 """
551
551
552 if os.path.isdir(dst):
552 if os.path.isdir(dst):
553 dst = os.path.join(dst, os.path.basename(src))
553 dst = os.path.join(dst, os.path.basename(src))
554
554
555 link_errno = link(src, dst)
555 link_errno = link(src, dst)
556 if link_errno == errno.EEXIST:
556 if link_errno == errno.EEXIST:
557 if os.stat(src).st_ino == os.stat(dst).st_ino:
558 # dst is already a hard link to the correct file, so we don't need
559 # to do anything else. If we try to link and rename the file
560 # anyway, we get duplicate files - see http://bugs.python.org/issue21876
561 return
562
557 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
563 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
558 try:
564 try:
559 link_or_copy(src, new_dst)
565 link_or_copy(src, new_dst)
560 except:
566 except:
561 try:
567 try:
562 os.remove(new_dst)
568 os.remove(new_dst)
563 except OSError:
569 except OSError:
564 pass
570 pass
565 raise
571 raise
566 os.rename(new_dst, dst)
572 os.rename(new_dst, dst)
567 elif link_errno != 0:
573 elif link_errno != 0:
568 # Either link isn't supported, or the filesystem doesn't support
574 # Either link isn't supported, or the filesystem doesn't support
569 # linking, or 'src' and 'dst' are on different filesystems.
575 # linking, or 'src' and 'dst' are on different filesystems.
570 shutil.copy(src, dst)
576 shutil.copy(src, dst)
571
577
572 def ensure_dir_exists(path, mode=0o755):
578 def ensure_dir_exists(path, mode=0o755):
573 """ensure that a directory exists
579 """ensure that a directory exists
574
580
575 If it doesn't exist, try to create it and protect against a race condition
581 If it doesn't exist, try to create it and protect against a race condition
576 if another process is doing the same.
582 if another process is doing the same.
577
583
578 The default permissions are 755, which differ from os.makedirs default of 777.
584 The default permissions are 755, which differ from os.makedirs default of 777.
579 """
585 """
580 if not os.path.exists(path):
586 if not os.path.exists(path):
581 try:
587 try:
582 os.makedirs(path, mode=mode)
588 os.makedirs(path, mode=mode)
583 except OSError as e:
589 except OSError as e:
584 if e.errno != errno.EEXIST:
590 if e.errno != errno.EEXIST:
585 raise
591 raise
586 elif not os.path.isdir(path):
592 elif not os.path.isdir(path):
587 raise IOError("%r exists but is not a directory" % path)
593 raise IOError("%r exists but is not a directory" % path)
@@ -1,678 +1,687 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
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 errno
7 import errno
8 import os
8 import os
9 import shutil
9 import shutil
10 import sys
10 import sys
11 import tempfile
11 import tempfile
12 import warnings
12 import warnings
13 from contextlib import contextmanager
13 from contextlib import contextmanager
14
14
15 try: # Python 3.3+
15 try: # Python 3.3+
16 from unittest.mock import patch
16 from unittest.mock import patch
17 except ImportError:
17 except ImportError:
18 from mock import patch
18 from mock import patch
19
19
20 from os.path import join, abspath, split
20 from os.path import join, abspath, split
21
21
22 from nose import SkipTest
22 from nose import SkipTest
23 import nose.tools as nt
23 import nose.tools as nt
24
24
25 from nose import with_setup
25 from nose import with_setup
26
26
27 import IPython
27 import IPython
28 from IPython.testing import decorators as dec
28 from IPython.testing import decorators as dec
29 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
29 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
30 onlyif_unicode_paths,)
30 onlyif_unicode_paths,)
31 from IPython.testing.tools import make_tempfile, AssertPrints
31 from IPython.testing.tools import make_tempfile, AssertPrints
32 from IPython.utils import path
32 from IPython.utils import path
33 from IPython.utils import py3compat
33 from IPython.utils import py3compat
34 from IPython.utils.tempdir import TemporaryDirectory
34 from IPython.utils.tempdir import TemporaryDirectory
35
35
36 # Platform-dependent imports
36 # Platform-dependent imports
37 try:
37 try:
38 import winreg as wreg # Py 3
38 import winreg as wreg # Py 3
39 except ImportError:
39 except ImportError:
40 try:
40 try:
41 import _winreg as wreg # Py 2
41 import _winreg as wreg # Py 2
42 except ImportError:
42 except ImportError:
43 #Fake _winreg module on none windows platforms
43 #Fake _winreg module on none windows platforms
44 import types
44 import types
45 wr_name = "winreg" if py3compat.PY3 else "_winreg"
45 wr_name = "winreg" if py3compat.PY3 else "_winreg"
46 sys.modules[wr_name] = types.ModuleType(wr_name)
46 sys.modules[wr_name] = types.ModuleType(wr_name)
47 try:
47 try:
48 import winreg as wreg
48 import winreg as wreg
49 except ImportError:
49 except ImportError:
50 import _winreg as wreg
50 import _winreg as wreg
51 #Add entries that needs to be stubbed by the testing code
51 #Add entries that needs to be stubbed by the testing code
52 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
52 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
53
53
54 try:
54 try:
55 reload
55 reload
56 except NameError: # Python 3
56 except NameError: # Python 3
57 from imp import reload
57 from imp import reload
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Globals
60 # Globals
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 env = os.environ
62 env = os.environ
63 TEST_FILE_PATH = split(abspath(__file__))[0]
63 TEST_FILE_PATH = split(abspath(__file__))[0]
64 TMP_TEST_DIR = tempfile.mkdtemp()
64 TMP_TEST_DIR = tempfile.mkdtemp()
65 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
65 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
66 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
66 XDG_TEST_DIR = join(HOME_TEST_DIR, "xdg_test_dir")
67 XDG_CACHE_DIR = join(HOME_TEST_DIR, "xdg_cache_dir")
67 XDG_CACHE_DIR = join(HOME_TEST_DIR, "xdg_cache_dir")
68 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
68 IP_TEST_DIR = join(HOME_TEST_DIR,'.ipython')
69 #
69 #
70 # Setup/teardown functions/decorators
70 # Setup/teardown functions/decorators
71 #
71 #
72
72
73 def setup():
73 def setup():
74 """Setup testenvironment for the module:
74 """Setup testenvironment for the module:
75
75
76 - Adds dummy home dir tree
76 - Adds dummy home dir tree
77 """
77 """
78 # Do not mask exceptions here. In particular, catching WindowsError is a
78 # Do not mask exceptions here. In particular, catching WindowsError is a
79 # problem because that exception is only defined on Windows...
79 # problem because that exception is only defined on Windows...
80 os.makedirs(IP_TEST_DIR)
80 os.makedirs(IP_TEST_DIR)
81 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
81 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
82 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
82 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
83
83
84
84
85 def teardown():
85 def teardown():
86 """Teardown testenvironment for the module:
86 """Teardown testenvironment for the module:
87
87
88 - Remove dummy home dir tree
88 - Remove dummy home dir tree
89 """
89 """
90 # Note: we remove the parent test dir, which is the root of all test
90 # Note: we remove the parent test dir, which is the root of all test
91 # subdirs we may have created. Use shutil instead of os.removedirs, so
91 # subdirs we may have created. Use shutil instead of os.removedirs, so
92 # that non-empty directories are all recursively removed.
92 # that non-empty directories are all recursively removed.
93 shutil.rmtree(TMP_TEST_DIR)
93 shutil.rmtree(TMP_TEST_DIR)
94
94
95
95
96 def setup_environment():
96 def setup_environment():
97 """Setup testenvironment for some functions that are tested
97 """Setup testenvironment for some functions that are tested
98 in this module. In particular this functions stores attributes
98 in this module. In particular this functions stores attributes
99 and other things that we need to stub in some test functions.
99 and other things that we need to stub in some test functions.
100 This needs to be done on a function level and not module level because
100 This needs to be done on a function level and not module level because
101 each testfunction needs a pristine environment.
101 each testfunction needs a pristine environment.
102 """
102 """
103 global oldstuff, platformstuff
103 global oldstuff, platformstuff
104 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
104 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
105
105
106 def teardown_environment():
106 def teardown_environment():
107 """Restore things that were remembered by the setup_environment function
107 """Restore things that were remembered by the setup_environment function
108 """
108 """
109 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
109 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
110 os.chdir(old_wd)
110 os.chdir(old_wd)
111 reload(path)
111 reload(path)
112
112
113 for key in list(env):
113 for key in list(env):
114 if key not in oldenv:
114 if key not in oldenv:
115 del env[key]
115 del env[key]
116 env.update(oldenv)
116 env.update(oldenv)
117 if hasattr(sys, 'frozen'):
117 if hasattr(sys, 'frozen'):
118 del sys.frozen
118 del sys.frozen
119
119
120 # Build decorator that uses the setup_environment/setup_environment
120 # Build decorator that uses the setup_environment/setup_environment
121 with_environment = with_setup(setup_environment, teardown_environment)
121 with_environment = with_setup(setup_environment, teardown_environment)
122
122
123 @contextmanager
123 @contextmanager
124 def patch_get_home_dir(dirpath):
124 def patch_get_home_dir(dirpath):
125 orig_get_home_dir = path.get_home_dir
125 orig_get_home_dir = path.get_home_dir
126 path.get_home_dir = lambda : dirpath
126 path.get_home_dir = lambda : dirpath
127 try:
127 try:
128 yield
128 yield
129 finally:
129 finally:
130 path.get_home_dir = orig_get_home_dir
130 path.get_home_dir = orig_get_home_dir
131
131
132 @skip_if_not_win32
132 @skip_if_not_win32
133 @with_environment
133 @with_environment
134 def test_get_home_dir_1():
134 def test_get_home_dir_1():
135 """Testcase for py2exe logic, un-compressed lib
135 """Testcase for py2exe logic, un-compressed lib
136 """
136 """
137 unfrozen = path.get_home_dir()
137 unfrozen = path.get_home_dir()
138 sys.frozen = True
138 sys.frozen = True
139
139
140 #fake filename for IPython.__init__
140 #fake filename for IPython.__init__
141 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
141 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
142
142
143 home_dir = path.get_home_dir()
143 home_dir = path.get_home_dir()
144 nt.assert_equal(home_dir, unfrozen)
144 nt.assert_equal(home_dir, unfrozen)
145
145
146
146
147 @skip_if_not_win32
147 @skip_if_not_win32
148 @with_environment
148 @with_environment
149 def test_get_home_dir_2():
149 def test_get_home_dir_2():
150 """Testcase for py2exe logic, compressed lib
150 """Testcase for py2exe logic, compressed lib
151 """
151 """
152 unfrozen = path.get_home_dir()
152 unfrozen = path.get_home_dir()
153 sys.frozen = True
153 sys.frozen = True
154 #fake filename for IPython.__init__
154 #fake filename for IPython.__init__
155 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
155 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
156
156
157 home_dir = path.get_home_dir(True)
157 home_dir = path.get_home_dir(True)
158 nt.assert_equal(home_dir, unfrozen)
158 nt.assert_equal(home_dir, unfrozen)
159
159
160
160
161 @with_environment
161 @with_environment
162 def test_get_home_dir_3():
162 def test_get_home_dir_3():
163 """get_home_dir() uses $HOME if set"""
163 """get_home_dir() uses $HOME if set"""
164 env["HOME"] = HOME_TEST_DIR
164 env["HOME"] = HOME_TEST_DIR
165 home_dir = path.get_home_dir(True)
165 home_dir = path.get_home_dir(True)
166 # get_home_dir expands symlinks
166 # get_home_dir expands symlinks
167 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
167 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
168
168
169
169
170 @with_environment
170 @with_environment
171 def test_get_home_dir_4():
171 def test_get_home_dir_4():
172 """get_home_dir() still works if $HOME is not set"""
172 """get_home_dir() still works if $HOME is not set"""
173
173
174 if 'HOME' in env: del env['HOME']
174 if 'HOME' in env: del env['HOME']
175 # this should still succeed, but we don't care what the answer is
175 # this should still succeed, but we don't care what the answer is
176 home = path.get_home_dir(False)
176 home = path.get_home_dir(False)
177
177
178 @with_environment
178 @with_environment
179 def test_get_home_dir_5():
179 def test_get_home_dir_5():
180 """raise HomeDirError if $HOME is specified, but not a writable dir"""
180 """raise HomeDirError if $HOME is specified, but not a writable dir"""
181 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
181 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
182 # set os.name = posix, to prevent My Documents fallback on Windows
182 # set os.name = posix, to prevent My Documents fallback on Windows
183 os.name = 'posix'
183 os.name = 'posix'
184 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
184 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
185
185
186 # Should we stub wreg fully so we can run the test on all platforms?
186 # Should we stub wreg fully so we can run the test on all platforms?
187 @skip_if_not_win32
187 @skip_if_not_win32
188 @with_environment
188 @with_environment
189 def test_get_home_dir_8():
189 def test_get_home_dir_8():
190 """Using registry hack for 'My Documents', os=='nt'
190 """Using registry hack for 'My Documents', os=='nt'
191
191
192 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
192 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
193 """
193 """
194 os.name = 'nt'
194 os.name = 'nt'
195 # Remove from stub environment all keys that may be set
195 # Remove from stub environment all keys that may be set
196 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
196 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
197 env.pop(key, None)
197 env.pop(key, None)
198
198
199 class key:
199 class key:
200 def Close(self):
200 def Close(self):
201 pass
201 pass
202
202
203 with patch.object(wreg, 'OpenKey', return_value=key()), \
203 with patch.object(wreg, 'OpenKey', return_value=key()), \
204 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
204 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
205 home_dir = path.get_home_dir()
205 home_dir = path.get_home_dir()
206 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
206 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
207
207
208
208
209 @with_environment
209 @with_environment
210 def test_get_ipython_dir_1():
210 def test_get_ipython_dir_1():
211 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
211 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
212 env_ipdir = os.path.join("someplace", ".ipython")
212 env_ipdir = os.path.join("someplace", ".ipython")
213 path._writable_dir = lambda path: True
213 path._writable_dir = lambda path: True
214 env['IPYTHONDIR'] = env_ipdir
214 env['IPYTHONDIR'] = env_ipdir
215 ipdir = path.get_ipython_dir()
215 ipdir = path.get_ipython_dir()
216 nt.assert_equal(ipdir, env_ipdir)
216 nt.assert_equal(ipdir, env_ipdir)
217
217
218
218
219 @with_environment
219 @with_environment
220 def test_get_ipython_dir_2():
220 def test_get_ipython_dir_2():
221 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
221 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
222 with patch_get_home_dir('someplace'):
222 with patch_get_home_dir('someplace'):
223 path.get_xdg_dir = lambda : None
223 path.get_xdg_dir = lambda : None
224 path._writable_dir = lambda path: True
224 path._writable_dir = lambda path: True
225 os.name = "posix"
225 os.name = "posix"
226 env.pop('IPYTHON_DIR', None)
226 env.pop('IPYTHON_DIR', None)
227 env.pop('IPYTHONDIR', None)
227 env.pop('IPYTHONDIR', None)
228 env.pop('XDG_CONFIG_HOME', None)
228 env.pop('XDG_CONFIG_HOME', None)
229 ipdir = path.get_ipython_dir()
229 ipdir = path.get_ipython_dir()
230 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
230 nt.assert_equal(ipdir, os.path.join("someplace", ".ipython"))
231
231
232 @with_environment
232 @with_environment
233 def test_get_ipython_dir_3():
233 def test_get_ipython_dir_3():
234 """test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist."""
234 """test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist."""
235 tmphome = TemporaryDirectory()
235 tmphome = TemporaryDirectory()
236 try:
236 try:
237 with patch_get_home_dir(tmphome.name):
237 with patch_get_home_dir(tmphome.name):
238 os.name = "posix"
238 os.name = "posix"
239 env.pop('IPYTHON_DIR', None)
239 env.pop('IPYTHON_DIR', None)
240 env.pop('IPYTHONDIR', None)
240 env.pop('IPYTHONDIR', None)
241 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
241 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
242
242
243 with warnings.catch_warnings(record=True) as w:
243 with warnings.catch_warnings(record=True) as w:
244 ipdir = path.get_ipython_dir()
244 ipdir = path.get_ipython_dir()
245
245
246 nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython"))
246 nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython"))
247 if sys.platform != 'darwin':
247 if sys.platform != 'darwin':
248 nt.assert_equal(len(w), 1)
248 nt.assert_equal(len(w), 1)
249 nt.assert_in('Moving', str(w[0]))
249 nt.assert_in('Moving', str(w[0]))
250 finally:
250 finally:
251 tmphome.cleanup()
251 tmphome.cleanup()
252
252
253 @with_environment
253 @with_environment
254 def test_get_ipython_dir_4():
254 def test_get_ipython_dir_4():
255 """test_get_ipython_dir_4, warn if XDG and home both exist."""
255 """test_get_ipython_dir_4, warn if XDG and home both exist."""
256 with patch_get_home_dir(HOME_TEST_DIR):
256 with patch_get_home_dir(HOME_TEST_DIR):
257 os.name = "posix"
257 os.name = "posix"
258 env.pop('IPYTHON_DIR', None)
258 env.pop('IPYTHON_DIR', None)
259 env.pop('IPYTHONDIR', None)
259 env.pop('IPYTHONDIR', None)
260 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
260 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
261 try:
261 try:
262 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
262 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
263 except OSError as e:
263 except OSError as e:
264 if e.errno != errno.EEXIST:
264 if e.errno != errno.EEXIST:
265 raise
265 raise
266
266
267 with warnings.catch_warnings(record=True) as w:
267 with warnings.catch_warnings(record=True) as w:
268 ipdir = path.get_ipython_dir()
268 ipdir = path.get_ipython_dir()
269
269
270 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython"))
270 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython"))
271 if sys.platform != 'darwin':
271 if sys.platform != 'darwin':
272 nt.assert_equal(len(w), 1)
272 nt.assert_equal(len(w), 1)
273 nt.assert_in('Ignoring', str(w[0]))
273 nt.assert_in('Ignoring', str(w[0]))
274
274
275 @with_environment
275 @with_environment
276 def test_get_ipython_dir_5():
276 def test_get_ipython_dir_5():
277 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
277 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
278 with patch_get_home_dir(HOME_TEST_DIR):
278 with patch_get_home_dir(HOME_TEST_DIR):
279 os.name = "posix"
279 os.name = "posix"
280 env.pop('IPYTHON_DIR', None)
280 env.pop('IPYTHON_DIR', None)
281 env.pop('IPYTHONDIR', None)
281 env.pop('IPYTHONDIR', None)
282 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
282 env['XDG_CONFIG_HOME'] = XDG_TEST_DIR
283 try:
283 try:
284 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
284 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
285 except OSError as e:
285 except OSError as e:
286 if e.errno != errno.ENOENT:
286 if e.errno != errno.ENOENT:
287 raise
287 raise
288 ipdir = path.get_ipython_dir()
288 ipdir = path.get_ipython_dir()
289 nt.assert_equal(ipdir, IP_TEST_DIR)
289 nt.assert_equal(ipdir, IP_TEST_DIR)
290
290
291 @with_environment
291 @with_environment
292 def test_get_ipython_dir_6():
292 def test_get_ipython_dir_6():
293 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
293 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
294 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
294 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
295 os.mkdir(xdg)
295 os.mkdir(xdg)
296 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
296 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
297 with patch_get_home_dir(HOME_TEST_DIR):
297 with patch_get_home_dir(HOME_TEST_DIR):
298 orig_get_xdg_dir = path.get_xdg_dir
298 orig_get_xdg_dir = path.get_xdg_dir
299 path.get_xdg_dir = lambda : xdg
299 path.get_xdg_dir = lambda : xdg
300 try:
300 try:
301 os.name = "posix"
301 os.name = "posix"
302 env.pop('IPYTHON_DIR', None)
302 env.pop('IPYTHON_DIR', None)
303 env.pop('IPYTHONDIR', None)
303 env.pop('IPYTHONDIR', None)
304 env.pop('XDG_CONFIG_HOME', None)
304 env.pop('XDG_CONFIG_HOME', None)
305 with warnings.catch_warnings(record=True) as w:
305 with warnings.catch_warnings(record=True) as w:
306 ipdir = path.get_ipython_dir()
306 ipdir = path.get_ipython_dir()
307
307
308 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython'))
308 nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython'))
309 nt.assert_equal(len(w), 0)
309 nt.assert_equal(len(w), 0)
310 finally:
310 finally:
311 path.get_xdg_dir = orig_get_xdg_dir
311 path.get_xdg_dir = orig_get_xdg_dir
312
312
313 @with_environment
313 @with_environment
314 def test_get_ipython_dir_7():
314 def test_get_ipython_dir_7():
315 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
315 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
316 path._writable_dir = lambda path: True
316 path._writable_dir = lambda path: True
317 home_dir = os.path.normpath(os.path.expanduser('~'))
317 home_dir = os.path.normpath(os.path.expanduser('~'))
318 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
318 env['IPYTHONDIR'] = os.path.join('~', 'somewhere')
319 ipdir = path.get_ipython_dir()
319 ipdir = path.get_ipython_dir()
320 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
320 nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere'))
321
321
322 @skip_win32
322 @skip_win32
323 @with_environment
323 @with_environment
324 def test_get_ipython_dir_8():
324 def test_get_ipython_dir_8():
325 """test_get_ipython_dir_8, test / home directory"""
325 """test_get_ipython_dir_8, test / home directory"""
326 old = path._writable_dir, path.get_xdg_dir
326 old = path._writable_dir, path.get_xdg_dir
327 try:
327 try:
328 path._writable_dir = lambda path: bool(path)
328 path._writable_dir = lambda path: bool(path)
329 path.get_xdg_dir = lambda: None
329 path.get_xdg_dir = lambda: None
330 env.pop('IPYTHON_DIR', None)
330 env.pop('IPYTHON_DIR', None)
331 env.pop('IPYTHONDIR', None)
331 env.pop('IPYTHONDIR', None)
332 env['HOME'] = '/'
332 env['HOME'] = '/'
333 nt.assert_equal(path.get_ipython_dir(), '/.ipython')
333 nt.assert_equal(path.get_ipython_dir(), '/.ipython')
334 finally:
334 finally:
335 path._writable_dir, path.get_xdg_dir = old
335 path._writable_dir, path.get_xdg_dir = old
336
336
337 @with_environment
337 @with_environment
338 def test_get_xdg_dir_0():
338 def test_get_xdg_dir_0():
339 """test_get_xdg_dir_0, check xdg_dir"""
339 """test_get_xdg_dir_0, check xdg_dir"""
340 reload(path)
340 reload(path)
341 path._writable_dir = lambda path: True
341 path._writable_dir = lambda path: True
342 path.get_home_dir = lambda : 'somewhere'
342 path.get_home_dir = lambda : 'somewhere'
343 os.name = "posix"
343 os.name = "posix"
344 sys.platform = "linux2"
344 sys.platform = "linux2"
345 env.pop('IPYTHON_DIR', None)
345 env.pop('IPYTHON_DIR', None)
346 env.pop('IPYTHONDIR', None)
346 env.pop('IPYTHONDIR', None)
347 env.pop('XDG_CONFIG_HOME', None)
347 env.pop('XDG_CONFIG_HOME', None)
348
348
349 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
349 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
350
350
351
351
352 @with_environment
352 @with_environment
353 def test_get_xdg_dir_1():
353 def test_get_xdg_dir_1():
354 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
354 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
355 reload(path)
355 reload(path)
356 path.get_home_dir = lambda : HOME_TEST_DIR
356 path.get_home_dir = lambda : HOME_TEST_DIR
357 os.name = "posix"
357 os.name = "posix"
358 sys.platform = "linux2"
358 sys.platform = "linux2"
359 env.pop('IPYTHON_DIR', None)
359 env.pop('IPYTHON_DIR', None)
360 env.pop('IPYTHONDIR', None)
360 env.pop('IPYTHONDIR', None)
361 env.pop('XDG_CONFIG_HOME', None)
361 env.pop('XDG_CONFIG_HOME', None)
362 nt.assert_equal(path.get_xdg_dir(), None)
362 nt.assert_equal(path.get_xdg_dir(), None)
363
363
364 @with_environment
364 @with_environment
365 def test_get_xdg_dir_2():
365 def test_get_xdg_dir_2():
366 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
366 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
367 reload(path)
367 reload(path)
368 path.get_home_dir = lambda : HOME_TEST_DIR
368 path.get_home_dir = lambda : HOME_TEST_DIR
369 os.name = "posix"
369 os.name = "posix"
370 sys.platform = "linux2"
370 sys.platform = "linux2"
371 env.pop('IPYTHON_DIR', None)
371 env.pop('IPYTHON_DIR', None)
372 env.pop('IPYTHONDIR', None)
372 env.pop('IPYTHONDIR', None)
373 env.pop('XDG_CONFIG_HOME', None)
373 env.pop('XDG_CONFIG_HOME', None)
374 cfgdir=os.path.join(path.get_home_dir(), '.config')
374 cfgdir=os.path.join(path.get_home_dir(), '.config')
375 if not os.path.exists(cfgdir):
375 if not os.path.exists(cfgdir):
376 os.makedirs(cfgdir)
376 os.makedirs(cfgdir)
377
377
378 nt.assert_equal(path.get_xdg_dir(), cfgdir)
378 nt.assert_equal(path.get_xdg_dir(), cfgdir)
379
379
380 @with_environment
380 @with_environment
381 def test_get_xdg_dir_3():
381 def test_get_xdg_dir_3():
382 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
382 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
383 reload(path)
383 reload(path)
384 path.get_home_dir = lambda : HOME_TEST_DIR
384 path.get_home_dir = lambda : HOME_TEST_DIR
385 os.name = "posix"
385 os.name = "posix"
386 sys.platform = "darwin"
386 sys.platform = "darwin"
387 env.pop('IPYTHON_DIR', None)
387 env.pop('IPYTHON_DIR', None)
388 env.pop('IPYTHONDIR', None)
388 env.pop('IPYTHONDIR', None)
389 env.pop('XDG_CONFIG_HOME', None)
389 env.pop('XDG_CONFIG_HOME', None)
390 cfgdir=os.path.join(path.get_home_dir(), '.config')
390 cfgdir=os.path.join(path.get_home_dir(), '.config')
391 if not os.path.exists(cfgdir):
391 if not os.path.exists(cfgdir):
392 os.makedirs(cfgdir)
392 os.makedirs(cfgdir)
393
393
394 nt.assert_equal(path.get_xdg_dir(), None)
394 nt.assert_equal(path.get_xdg_dir(), None)
395
395
396 def test_filefind():
396 def test_filefind():
397 """Various tests for filefind"""
397 """Various tests for filefind"""
398 f = tempfile.NamedTemporaryFile()
398 f = tempfile.NamedTemporaryFile()
399 # print 'fname:',f.name
399 # print 'fname:',f.name
400 alt_dirs = path.get_ipython_dir()
400 alt_dirs = path.get_ipython_dir()
401 t = path.filefind(f.name, alt_dirs)
401 t = path.filefind(f.name, alt_dirs)
402 # print 'found:',t
402 # print 'found:',t
403
403
404 @with_environment
404 @with_environment
405 def test_get_ipython_cache_dir():
405 def test_get_ipython_cache_dir():
406 os.environ["HOME"] = HOME_TEST_DIR
406 os.environ["HOME"] = HOME_TEST_DIR
407 if os.name == 'posix' and sys.platform != 'darwin':
407 if os.name == 'posix' and sys.platform != 'darwin':
408 # test default
408 # test default
409 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
409 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
410 os.environ.pop("XDG_CACHE_HOME", None)
410 os.environ.pop("XDG_CACHE_HOME", None)
411 ipdir = path.get_ipython_cache_dir()
411 ipdir = path.get_ipython_cache_dir()
412 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
412 nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"),
413 ipdir)
413 ipdir)
414 nt.assert_true(os.path.isdir(ipdir))
414 nt.assert_true(os.path.isdir(ipdir))
415
415
416 # test env override
416 # test env override
417 os.environ["XDG_CACHE_HOME"] = XDG_CACHE_DIR
417 os.environ["XDG_CACHE_HOME"] = XDG_CACHE_DIR
418 ipdir = path.get_ipython_cache_dir()
418 ipdir = path.get_ipython_cache_dir()
419 nt.assert_true(os.path.isdir(ipdir))
419 nt.assert_true(os.path.isdir(ipdir))
420 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
420 nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython"))
421 else:
421 else:
422 nt.assert_equal(path.get_ipython_cache_dir(),
422 nt.assert_equal(path.get_ipython_cache_dir(),
423 path.get_ipython_dir())
423 path.get_ipython_dir())
424
424
425 def test_get_ipython_package_dir():
425 def test_get_ipython_package_dir():
426 ipdir = path.get_ipython_package_dir()
426 ipdir = path.get_ipython_package_dir()
427 nt.assert_true(os.path.isdir(ipdir))
427 nt.assert_true(os.path.isdir(ipdir))
428
428
429
429
430 def test_get_ipython_module_path():
430 def test_get_ipython_module_path():
431 ipapp_path = path.get_ipython_module_path('IPython.terminal.ipapp')
431 ipapp_path = path.get_ipython_module_path('IPython.terminal.ipapp')
432 nt.assert_true(os.path.isfile(ipapp_path))
432 nt.assert_true(os.path.isfile(ipapp_path))
433
433
434
434
435 @dec.skip_if_not_win32
435 @dec.skip_if_not_win32
436 def test_get_long_path_name_win32():
436 def test_get_long_path_name_win32():
437 with TemporaryDirectory() as tmpdir:
437 with TemporaryDirectory() as tmpdir:
438
438
439 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
439 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
440 # path component, so ensure we include the long form of it
440 # path component, so ensure we include the long form of it
441 long_path = os.path.join(path.get_long_path_name(tmpdir), u'this is my long path name')
441 long_path = os.path.join(path.get_long_path_name(tmpdir), u'this is my long path name')
442 os.makedirs(long_path)
442 os.makedirs(long_path)
443
443
444 # Test to see if the short path evaluates correctly.
444 # Test to see if the short path evaluates correctly.
445 short_path = os.path.join(tmpdir, u'THISIS~1')
445 short_path = os.path.join(tmpdir, u'THISIS~1')
446 evaluated_path = path.get_long_path_name(short_path)
446 evaluated_path = path.get_long_path_name(short_path)
447 nt.assert_equal(evaluated_path.lower(), long_path.lower())
447 nt.assert_equal(evaluated_path.lower(), long_path.lower())
448
448
449
449
450 @dec.skip_win32
450 @dec.skip_win32
451 def test_get_long_path_name():
451 def test_get_long_path_name():
452 p = path.get_long_path_name('/usr/local')
452 p = path.get_long_path_name('/usr/local')
453 nt.assert_equal(p,'/usr/local')
453 nt.assert_equal(p,'/usr/local')
454
454
455 @dec.skip_win32 # can't create not-user-writable dir on win
455 @dec.skip_win32 # can't create not-user-writable dir on win
456 @with_environment
456 @with_environment
457 def test_not_writable_ipdir():
457 def test_not_writable_ipdir():
458 tmpdir = tempfile.mkdtemp()
458 tmpdir = tempfile.mkdtemp()
459 os.name = "posix"
459 os.name = "posix"
460 env.pop('IPYTHON_DIR', None)
460 env.pop('IPYTHON_DIR', None)
461 env.pop('IPYTHONDIR', None)
461 env.pop('IPYTHONDIR', None)
462 env.pop('XDG_CONFIG_HOME', None)
462 env.pop('XDG_CONFIG_HOME', None)
463 env['HOME'] = tmpdir
463 env['HOME'] = tmpdir
464 ipdir = os.path.join(tmpdir, '.ipython')
464 ipdir = os.path.join(tmpdir, '.ipython')
465 os.mkdir(ipdir, 0o555)
465 os.mkdir(ipdir, 0o555)
466 try:
466 try:
467 open(os.path.join(ipdir, "_foo_"), 'w').close()
467 open(os.path.join(ipdir, "_foo_"), 'w').close()
468 except IOError:
468 except IOError:
469 pass
469 pass
470 else:
470 else:
471 # I can still write to an unwritable dir,
471 # I can still write to an unwritable dir,
472 # assume I'm root and skip the test
472 # assume I'm root and skip the test
473 raise SkipTest("I can't create directories that I can't write to")
473 raise SkipTest("I can't create directories that I can't write to")
474 with AssertPrints('is not a writable location', channel='stderr'):
474 with AssertPrints('is not a writable location', channel='stderr'):
475 ipdir = path.get_ipython_dir()
475 ipdir = path.get_ipython_dir()
476 env.pop('IPYTHON_DIR', None)
476 env.pop('IPYTHON_DIR', None)
477
477
478 def test_unquote_filename():
478 def test_unquote_filename():
479 for win32 in (True, False):
479 for win32 in (True, False):
480 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
480 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
481 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
481 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
482 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
482 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
483 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
483 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
484 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
484 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
485 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
485 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
486 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
486 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
487 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
487 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
488 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
488 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
489 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
489 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
490
490
491 @with_environment
491 @with_environment
492 def test_get_py_filename():
492 def test_get_py_filename():
493 os.chdir(TMP_TEST_DIR)
493 os.chdir(TMP_TEST_DIR)
494 for win32 in (True, False):
494 for win32 in (True, False):
495 with make_tempfile('foo.py'):
495 with make_tempfile('foo.py'):
496 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
496 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
497 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
497 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
498 with make_tempfile('foo'):
498 with make_tempfile('foo'):
499 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
499 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
500 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
500 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
501 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
501 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
502 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
502 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
503 true_fn = 'foo with spaces.py'
503 true_fn = 'foo with spaces.py'
504 with make_tempfile(true_fn):
504 with make_tempfile(true_fn):
505 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
505 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
506 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
506 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
507 if win32:
507 if win32:
508 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
508 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
509 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
509 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
510 else:
510 else:
511 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
511 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
512 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
512 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
513
513
514 @onlyif_unicode_paths
514 @onlyif_unicode_paths
515 def test_unicode_in_filename():
515 def test_unicode_in_filename():
516 """When a file doesn't exist, the exception raised should be safe to call
516 """When a file doesn't exist, the exception raised should be safe to call
517 str() on - i.e. in Python 2 it must only have ASCII characters.
517 str() on - i.e. in Python 2 it must only have ASCII characters.
518
518
519 https://github.com/ipython/ipython/issues/875
519 https://github.com/ipython/ipython/issues/875
520 """
520 """
521 try:
521 try:
522 # these calls should not throw unicode encode exceptions
522 # these calls should not throw unicode encode exceptions
523 path.get_py_filename(u'fooéè.py', force_win32=False)
523 path.get_py_filename(u'fooéè.py', force_win32=False)
524 except IOError as ex:
524 except IOError as ex:
525 str(ex)
525 str(ex)
526
526
527
527
528 class TestShellGlob(object):
528 class TestShellGlob(object):
529
529
530 @classmethod
530 @classmethod
531 def setUpClass(cls):
531 def setUpClass(cls):
532 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
532 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
533 cls.filenames_end_with_b = ['0b', '1b', '2b']
533 cls.filenames_end_with_b = ['0b', '1b', '2b']
534 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
534 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
535 cls.tempdir = TemporaryDirectory()
535 cls.tempdir = TemporaryDirectory()
536 td = cls.tempdir.name
536 td = cls.tempdir.name
537
537
538 with cls.in_tempdir():
538 with cls.in_tempdir():
539 # Create empty files
539 # Create empty files
540 for fname in cls.filenames:
540 for fname in cls.filenames:
541 open(os.path.join(td, fname), 'w').close()
541 open(os.path.join(td, fname), 'w').close()
542
542
543 @classmethod
543 @classmethod
544 def tearDownClass(cls):
544 def tearDownClass(cls):
545 cls.tempdir.cleanup()
545 cls.tempdir.cleanup()
546
546
547 @classmethod
547 @classmethod
548 @contextmanager
548 @contextmanager
549 def in_tempdir(cls):
549 def in_tempdir(cls):
550 save = py3compat.getcwd()
550 save = py3compat.getcwd()
551 try:
551 try:
552 os.chdir(cls.tempdir.name)
552 os.chdir(cls.tempdir.name)
553 yield
553 yield
554 finally:
554 finally:
555 os.chdir(save)
555 os.chdir(save)
556
556
557 def check_match(self, patterns, matches):
557 def check_match(self, patterns, matches):
558 with self.in_tempdir():
558 with self.in_tempdir():
559 # glob returns unordered list. that's why sorted is required.
559 # glob returns unordered list. that's why sorted is required.
560 nt.assert_equals(sorted(path.shellglob(patterns)),
560 nt.assert_equals(sorted(path.shellglob(patterns)),
561 sorted(matches))
561 sorted(matches))
562
562
563 def common_cases(self):
563 def common_cases(self):
564 return [
564 return [
565 (['*'], self.filenames),
565 (['*'], self.filenames),
566 (['a*'], self.filenames_start_with_a),
566 (['a*'], self.filenames_start_with_a),
567 (['*c'], ['*c']),
567 (['*c'], ['*c']),
568 (['*', 'a*', '*b', '*c'], self.filenames
568 (['*', 'a*', '*b', '*c'], self.filenames
569 + self.filenames_start_with_a
569 + self.filenames_start_with_a
570 + self.filenames_end_with_b
570 + self.filenames_end_with_b
571 + ['*c']),
571 + ['*c']),
572 (['a[012]'], self.filenames_start_with_a),
572 (['a[012]'], self.filenames_start_with_a),
573 ]
573 ]
574
574
575 @skip_win32
575 @skip_win32
576 def test_match_posix(self):
576 def test_match_posix(self):
577 for (patterns, matches) in self.common_cases() + [
577 for (patterns, matches) in self.common_cases() + [
578 ([r'\*'], ['*']),
578 ([r'\*'], ['*']),
579 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
579 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
580 ([r'a\[012]'], ['a[012]']),
580 ([r'a\[012]'], ['a[012]']),
581 ]:
581 ]:
582 yield (self.check_match, patterns, matches)
582 yield (self.check_match, patterns, matches)
583
583
584 @skip_if_not_win32
584 @skip_if_not_win32
585 def test_match_windows(self):
585 def test_match_windows(self):
586 for (patterns, matches) in self.common_cases() + [
586 for (patterns, matches) in self.common_cases() + [
587 # In windows, backslash is interpreted as path
587 # In windows, backslash is interpreted as path
588 # separator. Therefore, you can't escape glob
588 # separator. Therefore, you can't escape glob
589 # using it.
589 # using it.
590 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
590 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
591 ([r'a\[012]'], [r'a\[012]']),
591 ([r'a\[012]'], [r'a\[012]']),
592 ]:
592 ]:
593 yield (self.check_match, patterns, matches)
593 yield (self.check_match, patterns, matches)
594
594
595
595
596 def test_unescape_glob():
596 def test_unescape_glob():
597 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
597 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
598 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
598 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
599 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
599 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
600 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
600 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
601 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
601 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
602
602
603
603
604 def test_ensure_dir_exists():
604 def test_ensure_dir_exists():
605 with TemporaryDirectory() as td:
605 with TemporaryDirectory() as td:
606 d = os.path.join(td, u'βˆ‚ir')
606 d = os.path.join(td, u'βˆ‚ir')
607 path.ensure_dir_exists(d) # create it
607 path.ensure_dir_exists(d) # create it
608 assert os.path.isdir(d)
608 assert os.path.isdir(d)
609 path.ensure_dir_exists(d) # no-op
609 path.ensure_dir_exists(d) # no-op
610 f = os.path.join(td, u'Ζ’ile')
610 f = os.path.join(td, u'Ζ’ile')
611 open(f, 'w').close() # touch
611 open(f, 'w').close() # touch
612 with nt.assert_raises(IOError):
612 with nt.assert_raises(IOError):
613 path.ensure_dir_exists(f)
613 path.ensure_dir_exists(f)
614
614
615 class TestLinkOrCopy(object):
615 class TestLinkOrCopy(object):
616 def setUp(self):
616 def setUp(self):
617 self.tempdir = TemporaryDirectory()
617 self.tempdir = TemporaryDirectory()
618 self.src = self.dst("src")
618 self.src = self.dst("src")
619 with open(self.src, "w") as f:
619 with open(self.src, "w") as f:
620 f.write("Hello, world!")
620 f.write("Hello, world!")
621
621
622 def tearDown(self):
622 def tearDown(self):
623 self.tempdir.cleanup()
623 self.tempdir.cleanup()
624
624
625 def dst(self, *args):
625 def dst(self, *args):
626 return os.path.join(self.tempdir.name, *args)
626 return os.path.join(self.tempdir.name, *args)
627
627
628 def assert_inode_not_equal(self, a, b):
628 def assert_inode_not_equal(self, a, b):
629 nt.assert_not_equals(os.stat(a).st_ino, os.stat(b).st_ino,
629 nt.assert_not_equals(os.stat(a).st_ino, os.stat(b).st_ino,
630 "%r and %r do reference the same indoes" %(a, b))
630 "%r and %r do reference the same indoes" %(a, b))
631
631
632 def assert_inode_equal(self, a, b):
632 def assert_inode_equal(self, a, b):
633 nt.assert_equals(os.stat(a).st_ino, os.stat(b).st_ino,
633 nt.assert_equals(os.stat(a).st_ino, os.stat(b).st_ino,
634 "%r and %r do not reference the same indoes" %(a, b))
634 "%r and %r do not reference the same indoes" %(a, b))
635
635
636 def assert_content_equal(self, a, b):
636 def assert_content_equal(self, a, b):
637 with open(a) as a_f:
637 with open(a) as a_f:
638 with open(b) as b_f:
638 with open(b) as b_f:
639 nt.assert_equals(a_f.read(), b_f.read())
639 nt.assert_equals(a_f.read(), b_f.read())
640
640
641 @skip_win32
641 @skip_win32
642 def test_link_successful(self):
642 def test_link_successful(self):
643 dst = self.dst("target")
643 dst = self.dst("target")
644 path.link_or_copy(self.src, dst)
644 path.link_or_copy(self.src, dst)
645 self.assert_inode_equal(self.src, dst)
645 self.assert_inode_equal(self.src, dst)
646
646
647 @skip_win32
647 @skip_win32
648 def test_link_into_dir(self):
648 def test_link_into_dir(self):
649 dst = self.dst("some_dir")
649 dst = self.dst("some_dir")
650 os.mkdir(dst)
650 os.mkdir(dst)
651 path.link_or_copy(self.src, dst)
651 path.link_or_copy(self.src, dst)
652 expected_dst = self.dst("some_dir", os.path.basename(self.src))
652 expected_dst = self.dst("some_dir", os.path.basename(self.src))
653 self.assert_inode_equal(self.src, expected_dst)
653 self.assert_inode_equal(self.src, expected_dst)
654
654
655 @skip_win32
655 @skip_win32
656 def test_target_exists(self):
656 def test_target_exists(self):
657 dst = self.dst("target")
657 dst = self.dst("target")
658 open(dst, "w").close()
658 open(dst, "w").close()
659 path.link_or_copy(self.src, dst)
659 path.link_or_copy(self.src, dst)
660 self.assert_inode_equal(self.src, dst)
660 self.assert_inode_equal(self.src, dst)
661
661
662 @skip_win32
662 @skip_win32
663 def test_no_link(self):
663 def test_no_link(self):
664 real_link = os.link
664 real_link = os.link
665 try:
665 try:
666 del os.link
666 del os.link
667 dst = self.dst("target")
667 dst = self.dst("target")
668 path.link_or_copy(self.src, dst)
668 path.link_or_copy(self.src, dst)
669 self.assert_content_equal(self.src, dst)
669 self.assert_content_equal(self.src, dst)
670 self.assert_inode_not_equal(self.src, dst)
670 self.assert_inode_not_equal(self.src, dst)
671 finally:
671 finally:
672 os.link = real_link
672 os.link = real_link
673
673
674 @skip_if_not_win32
674 @skip_if_not_win32
675 def test_windows(self):
675 def test_windows(self):
676 dst = self.dst("target")
676 dst = self.dst("target")
677 path.link_or_copy(self.src, dst)
677 path.link_or_copy(self.src, dst)
678 self.assert_content_equal(self.src, dst)
678 self.assert_content_equal(self.src, dst)
679
680 def test_link_twice(self):
681 # Linking the same file twice shouldn't leave duplicates around.
682 # See https://github.com/ipython/ipython/issues/6450
683 dst = self.dst('target')
684 path.link_or_copy(self.src, dst)
685 path.link_or_copy(self.src, dst)
686 self.assert_inode_equal(self.src, dst)
687 nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target'])
General Comments 0
You need to be logged in to leave comments. Login now