##// END OF EJS Templates
correct patching ?
Matthias Bussonnier -
Show More
@@ -1,437 +1,436 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import os
10 10 import sys
11 11 import errno
12 12 import shutil
13 13 import random
14 14 import glob
15 15 from warnings import warn
16 16
17 17 from IPython.utils.process import system
18 18 from IPython.utils import py3compat
19 19 from IPython.utils.decorators import undoc
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Code
23 23 #-----------------------------------------------------------------------------
24 24
25 25 fs_encoding = sys.getfilesystemencoding()
26 26
27 27 def _writable_dir(path):
28 28 """Whether `path` is a directory, to which the user has write access."""
29 29 return os.path.isdir(path) and os.access(path, os.W_OK)
30 30
31 31 if sys.platform == 'win32':
32 32 def _get_long_path_name(path):
33 33 """Get a long path name (expand ~) on Windows using ctypes.
34 34
35 35 Examples
36 36 --------
37 37
38 38 >>> get_long_path_name('c:\\docume~1')
39 39 'c:\\\\Documents and Settings'
40 40
41 41 """
42 42 try:
43 43 import ctypes
44 44 except ImportError:
45 45 raise ImportError('you need to have ctypes installed for this to work')
46 46 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
47 47 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
48 48 ctypes.c_uint ]
49 49
50 50 buf = ctypes.create_unicode_buffer(260)
51 51 rv = _GetLongPathName(path, buf, 260)
52 52 if rv == 0 or rv > 260:
53 53 return path
54 54 else:
55 55 return buf.value
56 56 else:
57 57 def _get_long_path_name(path):
58 58 """Dummy no-op."""
59 59 return path
60 60
61 61
62 62
63 63 def get_long_path_name(path):
64 64 """Expand a path into its long form.
65 65
66 66 On Windows this expands any ~ in the paths. On other platforms, it is
67 67 a null operation.
68 68 """
69 69 return _get_long_path_name(path)
70 70
71 71
72 72 def unquote_filename(name, win32=(sys.platform=='win32')):
73 73 """ On Windows, remove leading and trailing quotes from filenames.
74 74
75 75 This function has been deprecated and should not be used any more:
76 76 unquoting is now taken care of by :func:`IPython.utils.process.arg_split`.
77 77 """
78 78 warn("'unquote_filename' is deprecated since IPython 5.0 and should not "
79 79 "be used anymore", DeprecationWarning, stacklevel=2)
80 80 if win32:
81 81 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
82 82 name = name[1:-1]
83 83 return name
84 84
85 85
86 86 def compress_user(path):
87 87 """Reverse of :func:`os.path.expanduser`
88 88 """
89 89 home = os.path.expanduser('~')
90 90 if path.startswith(home):
91 91 path = "~" + path[len(home):]
92 92 return path
93 93
94 94 def get_py_filename(name, force_win32=None):
95 95 """Return a valid python filename in the current directory.
96 96
97 97 If the given name is not a file, it adds '.py' and searches again.
98 98 Raises IOError with an informative message if the file isn't found.
99 99 """
100 100
101 101 name = os.path.expanduser(name)
102 102 if force_win32 is not None:
103 103 warn("The 'force_win32' argument to 'get_py_filename' is deprecated "
104 104 "since IPython 5.0 and should not be used anymore",
105 105 DeprecationWarning, stacklevel=2)
106 106 if not os.path.isfile(name) and not name.endswith('.py'):
107 107 name += '.py'
108 108 if os.path.isfile(name):
109 109 return name
110 110 else:
111 111 raise IOError('File `%r` not found.' % name)
112 112
113 113
114 114 def filefind(filename, path_dirs=None):
115 115 """Find a file by looking through a sequence of paths.
116 116
117 117 This iterates through a sequence of paths looking for a file and returns
118 118 the full, absolute path of the first occurrence of the file. If no set of
119 119 path dirs is given, the filename is tested as is, after running through
120 120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
121 121
122 122 filefind('myfile.txt')
123 123
124 124 will find the file in the current working dir, but::
125 125
126 126 filefind('~/myfile.txt')
127 127
128 128 Will find the file in the users home directory. This function does not
129 129 automatically try any paths, such as the cwd or the user's home directory.
130 130
131 131 Parameters
132 132 ----------
133 133 filename : str
134 134 The filename to look for.
135 135 path_dirs : str, None or sequence of str
136 136 The sequence of paths to look for the file in. If None, the filename
137 137 need to be absolute or be in the cwd. If a string, the string is
138 138 put into a sequence and the searched. If a sequence, walk through
139 139 each element and join with ``filename``, calling :func:`expandvars`
140 140 and :func:`expanduser` before testing for existence.
141 141
142 142 Returns
143 143 -------
144 144 Raises :exc:`IOError` or returns absolute path to file.
145 145 """
146 146
147 147 # If paths are quoted, abspath gets confused, strip them...
148 148 filename = filename.strip('"').strip("'")
149 149 # If the input is an absolute path, just check it exists
150 150 if os.path.isabs(filename) and os.path.isfile(filename):
151 151 return filename
152 152
153 153 if path_dirs is None:
154 154 path_dirs = ("",)
155 155 elif isinstance(path_dirs, str):
156 156 path_dirs = (path_dirs,)
157 157
158 158 for path in path_dirs:
159 159 if path == '.': path = os.getcwd()
160 160 testname = expand_path(os.path.join(path, filename))
161 161 if os.path.isfile(testname):
162 162 return os.path.abspath(testname)
163 163
164 164 raise IOError("File %r does not exist in any of the search paths: %r" %
165 165 (filename, path_dirs) )
166 166
167 167
168 168 class HomeDirError(Exception):
169 169 pass
170 170
171 171
172 172 def get_home_dir(require_writable=False) -> str:
173 173 """Return the 'home' directory, as a unicode string.
174 174
175 175 Uses os.path.expanduser('~'), and checks for writability.
176 176
177 177 See stdlib docs for how this is determined.
178 178 For Python <3.8, $HOME is first priority on *ALL* platforms.
179 179 For Python >=3.8 on Windows, %HOME% is no longer considered.
180 180
181 181 Parameters
182 182 ----------
183 183
184 184 require_writable : bool [default: False]
185 185 if True:
186 186 guarantees the return value is a writable directory, otherwise
187 187 raises HomeDirError
188 188 if False:
189 189 The path is resolved, but it is not guaranteed to exist or be writable.
190 190 """
191 191
192 192 homedir = os.path.expanduser('~')
193 193 # Next line will make things work even when /home/ is a symlink to
194 194 # /usr/home as it is on FreeBSD, for example
195 195 homedir = os.path.realpath(homedir)
196 196
197 197 if not _writable_dir(homedir) and os.name == 'nt':
198 198 # expanduser failed, use the registry to get the 'My Documents' folder.
199 199 try:
200 200 import winreg as wreg
201 key = wreg.OpenKey(
201 with wreg.OpenKey(
202 202 wreg.HKEY_CURRENT_USER,
203 203 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
204 )
205 homedir = wreg.QueryValueEx(key,'Personal')[0]
206 key.Close()
204 ) as key:
205 homedir = wreg.QueryValueEx(key,'Personal')[0]
207 206 except:
208 207 pass
209 208
210 209 if (not require_writable) or _writable_dir(homedir):
211 210 assert isinstance(homedir, str), "Homedir shoudl be unicode not bytes"
212 211 return homedir
213 212 else:
214 213 raise HomeDirError('%s is not a writable dir, '
215 214 'set $HOME environment variable to override' % homedir)
216 215
217 216 def get_xdg_dir():
218 217 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
219 218
220 219 This is only for non-OS X posix (Linux,Unix,etc.) systems.
221 220 """
222 221
223 222 env = os.environ
224 223
225 224 if os.name == 'posix' and sys.platform != 'darwin':
226 225 # Linux, Unix, AIX, etc.
227 226 # use ~/.config if empty OR not set
228 227 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
229 228 if xdg and _writable_dir(xdg):
230 229 return py3compat.cast_unicode(xdg, fs_encoding)
231 230
232 231 return None
233 232
234 233
235 234 def get_xdg_cache_dir():
236 235 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
237 236
238 237 This is only for non-OS X posix (Linux,Unix,etc.) systems.
239 238 """
240 239
241 240 env = os.environ
242 241
243 242 if os.name == 'posix' and sys.platform != 'darwin':
244 243 # Linux, Unix, AIX, etc.
245 244 # use ~/.cache if empty OR not set
246 245 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
247 246 if xdg and _writable_dir(xdg):
248 247 return py3compat.cast_unicode(xdg, fs_encoding)
249 248
250 249 return None
251 250
252 251
253 252 @undoc
254 253 def get_ipython_dir():
255 254 warn("get_ipython_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
256 255 from IPython.paths import get_ipython_dir
257 256 return get_ipython_dir()
258 257
259 258 @undoc
260 259 def get_ipython_cache_dir():
261 260 warn("get_ipython_cache_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
262 261 from IPython.paths import get_ipython_cache_dir
263 262 return get_ipython_cache_dir()
264 263
265 264 @undoc
266 265 def get_ipython_package_dir():
267 266 warn("get_ipython_package_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
268 267 from IPython.paths import get_ipython_package_dir
269 268 return get_ipython_package_dir()
270 269
271 270 @undoc
272 271 def get_ipython_module_path(module_str):
273 272 warn("get_ipython_module_path has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
274 273 from IPython.paths import get_ipython_module_path
275 274 return get_ipython_module_path(module_str)
276 275
277 276 @undoc
278 277 def locate_profile(profile='default'):
279 278 warn("locate_profile has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
280 279 from IPython.paths import locate_profile
281 280 return locate_profile(profile=profile)
282 281
283 282 def expand_path(s):
284 283 """Expand $VARS and ~names in a string, like a shell
285 284
286 285 :Examples:
287 286
288 287 In [2]: os.environ['FOO']='test'
289 288
290 289 In [3]: expand_path('variable FOO is $FOO')
291 290 Out[3]: 'variable FOO is test'
292 291 """
293 292 # This is a pretty subtle hack. When expand user is given a UNC path
294 293 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
295 294 # the $ to get (\\server\share\%username%). I think it considered $
296 295 # alone an empty var. But, we need the $ to remains there (it indicates
297 296 # a hidden share).
298 297 if os.name=='nt':
299 298 s = s.replace('$\\', 'IPYTHON_TEMP')
300 299 s = os.path.expandvars(os.path.expanduser(s))
301 300 if os.name=='nt':
302 301 s = s.replace('IPYTHON_TEMP', '$\\')
303 302 return s
304 303
305 304
306 305 def unescape_glob(string):
307 306 """Unescape glob pattern in `string`."""
308 307 def unescape(s):
309 308 for pattern in '*[]!?':
310 309 s = s.replace(r'\{0}'.format(pattern), pattern)
311 310 return s
312 311 return '\\'.join(map(unescape, string.split('\\\\')))
313 312
314 313
315 314 def shellglob(args):
316 315 """
317 316 Do glob expansion for each element in `args` and return a flattened list.
318 317
319 318 Unmatched glob pattern will remain as-is in the returned list.
320 319
321 320 """
322 321 expanded = []
323 322 # Do not unescape backslash in Windows as it is interpreted as
324 323 # path separator:
325 324 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
326 325 for a in args:
327 326 expanded.extend(glob.glob(a) or [unescape(a)])
328 327 return expanded
329 328
330 329
331 330 def target_outdated(target,deps):
332 331 """Determine whether a target is out of date.
333 332
334 333 target_outdated(target,deps) -> 1/0
335 334
336 335 deps: list of filenames which MUST exist.
337 336 target: single filename which may or may not exist.
338 337
339 338 If target doesn't exist or is older than any file listed in deps, return
340 339 true, otherwise return false.
341 340 """
342 341 try:
343 342 target_time = os.path.getmtime(target)
344 343 except os.error:
345 344 return 1
346 345 for dep in deps:
347 346 dep_time = os.path.getmtime(dep)
348 347 if dep_time > target_time:
349 348 #print "For target",target,"Dep failed:",dep # dbg
350 349 #print "times (dep,tar):",dep_time,target_time # dbg
351 350 return 1
352 351 return 0
353 352
354 353
355 354 def target_update(target,deps,cmd):
356 355 """Update a target with a given command given a list of dependencies.
357 356
358 357 target_update(target,deps,cmd) -> runs cmd if target is outdated.
359 358
360 359 This is just a wrapper around target_outdated() which calls the given
361 360 command if target is outdated."""
362 361
363 362 if target_outdated(target,deps):
364 363 system(cmd)
365 364
366 365
367 366 ENOLINK = 1998
368 367
369 368 def link(src, dst):
370 369 """Hard links ``src`` to ``dst``, returning 0 or errno.
371 370
372 371 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
373 372 supported by the operating system.
374 373 """
375 374
376 375 if not hasattr(os, "link"):
377 376 return ENOLINK
378 377 link_errno = 0
379 378 try:
380 379 os.link(src, dst)
381 380 except OSError as e:
382 381 link_errno = e.errno
383 382 return link_errno
384 383
385 384
386 385 def link_or_copy(src, dst):
387 386 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
388 387
389 388 Attempts to maintain the semantics of ``shutil.copy``.
390 389
391 390 Because ``os.link`` does not overwrite files, a unique temporary file
392 391 will be used if the target already exists, then that file will be moved
393 392 into place.
394 393 """
395 394
396 395 if os.path.isdir(dst):
397 396 dst = os.path.join(dst, os.path.basename(src))
398 397
399 398 link_errno = link(src, dst)
400 399 if link_errno == errno.EEXIST:
401 400 if os.stat(src).st_ino == os.stat(dst).st_ino:
402 401 # dst is already a hard link to the correct file, so we don't need
403 402 # to do anything else. If we try to link and rename the file
404 403 # anyway, we get duplicate files - see http://bugs.python.org/issue21876
405 404 return
406 405
407 406 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
408 407 try:
409 408 link_or_copy(src, new_dst)
410 409 except:
411 410 try:
412 411 os.remove(new_dst)
413 412 except OSError:
414 413 pass
415 414 raise
416 415 os.rename(new_dst, dst)
417 416 elif link_errno != 0:
418 417 # Either link isn't supported, or the filesystem doesn't support
419 418 # linking, or 'src' and 'dst' are on different filesystems.
420 419 shutil.copy(src, dst)
421 420
422 421 def ensure_dir_exists(path, mode=0o755):
423 422 """ensure that a directory exists
424 423
425 424 If it doesn't exist, try to create it and protect against a race condition
426 425 if another process is doing the same.
427 426
428 427 The default permissions are 755, which differ from os.makedirs default of 777.
429 428 """
430 429 if not os.path.exists(path):
431 430 try:
432 431 os.makedirs(path, mode=mode)
433 432 except OSError as e:
434 433 if e.errno != errno.EEXIST:
435 434 raise
436 435 elif not os.path.isdir(path):
437 436 raise IOError("%r exists but is not a directory" % path)
@@ -1,488 +1,492 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.path.py"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import shutil
9 9 import sys
10 10 import tempfile
11 11 import unittest
12 12 from contextlib import contextmanager
13 13 from unittest.mock import patch
14 14 from os.path import join, abspath
15 15 from imp import reload
16 16
17 17 from nose import SkipTest, with_setup
18 18 import nose.tools as nt
19 19
20 20 import IPython
21 21 from IPython import paths
22 22 from IPython.testing import decorators as dec
23 23 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
24 24 onlyif_unicode_paths, skipif,
25 25 skip_win32_py38,)
26 26 from IPython.testing.tools import make_tempfile, AssertPrints
27 27 from IPython.utils import path
28 28 from IPython.utils.tempdir import TemporaryDirectory
29 29
30 30
31 31 # Platform-dependent imports
32 32 try:
33 33 import winreg as wreg
34 34 except ImportError:
35 35 #Fake _winreg module on non-windows platforms
36 36 import types
37 37 wr_name = "winreg"
38 38 sys.modules[wr_name] = types.ModuleType(wr_name)
39 39 try:
40 40 import winreg as wreg
41 41 except ImportError:
42 42 import _winreg as wreg
43 43 #Add entries that needs to be stubbed by the testing code
44 44 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Globals
48 48 #-----------------------------------------------------------------------------
49 49 env = os.environ
50 50 TMP_TEST_DIR = tempfile.mkdtemp()
51 51 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
52 52 #
53 53 # Setup/teardown functions/decorators
54 54 #
55 55
56 56 def setup_module():
57 57 """Setup testenvironment for the module:
58 58
59 59 - Adds dummy home dir tree
60 60 """
61 61 # Do not mask exceptions here. In particular, catching WindowsError is a
62 62 # problem because that exception is only defined on Windows...
63 63 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
64 64
65 65
66 66 def teardown_module():
67 67 """Teardown testenvironment for the module:
68 68
69 69 - Remove dummy home dir tree
70 70 """
71 71 # Note: we remove the parent test dir, which is the root of all test
72 72 # subdirs we may have created. Use shutil instead of os.removedirs, so
73 73 # that non-empty directories are all recursively removed.
74 74 shutil.rmtree(TMP_TEST_DIR)
75 75
76 76
77 77 def setup_environment():
78 78 """Setup testenvironment for some functions that are tested
79 79 in this module. In particular this functions stores attributes
80 80 and other things that we need to stub in some test functions.
81 81 This needs to be done on a function level and not module level because
82 82 each testfunction needs a pristine environment.
83 83 """
84 84 global oldstuff, platformstuff
85 85 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
86 86
87 87 def teardown_environment():
88 88 """Restore things that were remembered by the setup_environment function
89 89 """
90 90 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
91 91 os.chdir(old_wd)
92 92 reload(path)
93 93
94 94 for key in list(env):
95 95 if key not in oldenv:
96 96 del env[key]
97 97 env.update(oldenv)
98 98 if hasattr(sys, 'frozen'):
99 99 del sys.frozen
100 100
101 101 # Build decorator that uses the setup_environment/setup_environment
102 102 with_environment = with_setup(setup_environment, teardown_environment)
103 103
104 104 @skip_if_not_win32
105 105 @with_environment
106 106 def test_get_home_dir_1():
107 107 """Testcase for py2exe logic, un-compressed lib
108 108 """
109 109 unfrozen = path.get_home_dir()
110 110 sys.frozen = True
111 111
112 112 #fake filename for IPython.__init__
113 113 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
114 114
115 115 home_dir = path.get_home_dir()
116 116 nt.assert_equal(home_dir, unfrozen)
117 117
118 118
119 119 @skip_if_not_win32
120 120 @with_environment
121 121 def test_get_home_dir_2():
122 122 """Testcase for py2exe logic, compressed lib
123 123 """
124 124 unfrozen = path.get_home_dir()
125 125 sys.frozen = True
126 126 #fake filename for IPython.__init__
127 127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
128 128
129 129 home_dir = path.get_home_dir(True)
130 130 nt.assert_equal(home_dir, unfrozen)
131 131
132 132
133 133 @skip_win32_py38
134 134 @with_environment
135 135 def test_get_home_dir_3():
136 136 """get_home_dir() uses $HOME if set"""
137 137 env["HOME"] = HOME_TEST_DIR
138 138 home_dir = path.get_home_dir(True)
139 139 # get_home_dir expands symlinks
140 140 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
141 141
142 142
143 143 @with_environment
144 144 def test_get_home_dir_4():
145 145 """get_home_dir() still works if $HOME is not set"""
146 146
147 147 if 'HOME' in env: del env['HOME']
148 148 # this should still succeed, but we don't care what the answer is
149 149 home = path.get_home_dir(False)
150 150
151 151 @skip_win32_py38
152 152 @with_environment
153 153 def test_get_home_dir_5():
154 154 """raise HomeDirError if $HOME is specified, but not a writable dir"""
155 155 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
156 156 # set os.name = posix, to prevent My Documents fallback on Windows
157 157 os.name = 'posix'
158 158 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
159 159
160 160 # Should we stub wreg fully so we can run the test on all platforms?
161 161 @skip_if_not_win32
162 162 @with_environment
163 163 def test_get_home_dir_8():
164 164 """Using registry hack for 'My Documents', os=='nt'
165 165
166 166 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
167 167 """
168 168 os.name = 'nt'
169 169 # Remove from stub environment all keys that may be set
170 170 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
171 171 env.pop(key, None)
172 172
173 173 class key:
174 def __enter__(self):
175 pass
174 176 def Close(self):
175 177 pass
178 def __exit__(*args, **kwargs):
179 pass
176 180
177 181 with patch.object(wreg, 'OpenKey', return_value=key()), \
178 182 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
179 183 home_dir = path.get_home_dir()
180 184 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
181 185
182 186 @with_environment
183 187 def test_get_xdg_dir_0():
184 188 """test_get_xdg_dir_0, check xdg_dir"""
185 189 reload(path)
186 190 path._writable_dir = lambda path: True
187 191 path.get_home_dir = lambda : 'somewhere'
188 192 os.name = "posix"
189 193 sys.platform = "linux2"
190 194 env.pop('IPYTHON_DIR', None)
191 195 env.pop('IPYTHONDIR', None)
192 196 env.pop('XDG_CONFIG_HOME', None)
193 197
194 198 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
195 199
196 200
197 201 @with_environment
198 202 def test_get_xdg_dir_1():
199 203 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
200 204 reload(path)
201 205 path.get_home_dir = lambda : HOME_TEST_DIR
202 206 os.name = "posix"
203 207 sys.platform = "linux2"
204 208 env.pop('IPYTHON_DIR', None)
205 209 env.pop('IPYTHONDIR', None)
206 210 env.pop('XDG_CONFIG_HOME', None)
207 211 nt.assert_equal(path.get_xdg_dir(), None)
208 212
209 213 @with_environment
210 214 def test_get_xdg_dir_2():
211 215 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
212 216 reload(path)
213 217 path.get_home_dir = lambda : HOME_TEST_DIR
214 218 os.name = "posix"
215 219 sys.platform = "linux2"
216 220 env.pop('IPYTHON_DIR', None)
217 221 env.pop('IPYTHONDIR', None)
218 222 env.pop('XDG_CONFIG_HOME', None)
219 223 cfgdir=os.path.join(path.get_home_dir(), '.config')
220 224 if not os.path.exists(cfgdir):
221 225 os.makedirs(cfgdir)
222 226
223 227 nt.assert_equal(path.get_xdg_dir(), cfgdir)
224 228
225 229 @with_environment
226 230 def test_get_xdg_dir_3():
227 231 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
228 232 reload(path)
229 233 path.get_home_dir = lambda : HOME_TEST_DIR
230 234 os.name = "posix"
231 235 sys.platform = "darwin"
232 236 env.pop('IPYTHON_DIR', None)
233 237 env.pop('IPYTHONDIR', None)
234 238 env.pop('XDG_CONFIG_HOME', None)
235 239 cfgdir=os.path.join(path.get_home_dir(), '.config')
236 240 if not os.path.exists(cfgdir):
237 241 os.makedirs(cfgdir)
238 242
239 243 nt.assert_equal(path.get_xdg_dir(), None)
240 244
241 245 def test_filefind():
242 246 """Various tests for filefind"""
243 247 f = tempfile.NamedTemporaryFile()
244 248 # print 'fname:',f.name
245 249 alt_dirs = paths.get_ipython_dir()
246 250 t = path.filefind(f.name, alt_dirs)
247 251 # print 'found:',t
248 252
249 253
250 254 @dec.skip_if_not_win32
251 255 def test_get_long_path_name_win32():
252 256 with TemporaryDirectory() as tmpdir:
253 257
254 258 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
255 259 # path component, so ensure we include the long form of it
256 260 long_path = os.path.join(path.get_long_path_name(tmpdir), 'this is my long path name')
257 261 os.makedirs(long_path)
258 262
259 263 # Test to see if the short path evaluates correctly.
260 264 short_path = os.path.join(tmpdir, 'THISIS~1')
261 265 evaluated_path = path.get_long_path_name(short_path)
262 266 nt.assert_equal(evaluated_path.lower(), long_path.lower())
263 267
264 268
265 269 @dec.skip_win32
266 270 def test_get_long_path_name():
267 271 p = path.get_long_path_name('/usr/local')
268 272 nt.assert_equal(p,'/usr/local')
269 273
270 274
271 275 class TestRaiseDeprecation(unittest.TestCase):
272 276
273 277 @dec.skip_win32 # can't create not-user-writable dir on win
274 278 @with_environment
275 279 def test_not_writable_ipdir(self):
276 280 tmpdir = tempfile.mkdtemp()
277 281 os.name = "posix"
278 282 env.pop('IPYTHON_DIR', None)
279 283 env.pop('IPYTHONDIR', None)
280 284 env.pop('XDG_CONFIG_HOME', None)
281 285 env['HOME'] = tmpdir
282 286 ipdir = os.path.join(tmpdir, '.ipython')
283 287 os.mkdir(ipdir, 0o555)
284 288 try:
285 289 open(os.path.join(ipdir, "_foo_"), 'w').close()
286 290 except IOError:
287 291 pass
288 292 else:
289 293 # I can still write to an unwritable dir,
290 294 # assume I'm root and skip the test
291 295 raise SkipTest("I can't create directories that I can't write to")
292 296 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
293 297 ipdir = paths.get_ipython_dir()
294 298 env.pop('IPYTHON_DIR', None)
295 299
296 300 @with_environment
297 301 def test_get_py_filename():
298 302 os.chdir(TMP_TEST_DIR)
299 303 with make_tempfile('foo.py'):
300 304 nt.assert_equal(path.get_py_filename('foo.py'), 'foo.py')
301 305 nt.assert_equal(path.get_py_filename('foo'), 'foo.py')
302 306 with make_tempfile('foo'):
303 307 nt.assert_equal(path.get_py_filename('foo'), 'foo')
304 308 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
305 309 nt.assert_raises(IOError, path.get_py_filename, 'foo')
306 310 nt.assert_raises(IOError, path.get_py_filename, 'foo.py')
307 311 true_fn = 'foo with spaces.py'
308 312 with make_tempfile(true_fn):
309 313 nt.assert_equal(path.get_py_filename('foo with spaces'), true_fn)
310 314 nt.assert_equal(path.get_py_filename('foo with spaces.py'), true_fn)
311 315 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"')
312 316 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'")
313 317
314 318 @onlyif_unicode_paths
315 319 def test_unicode_in_filename():
316 320 """When a file doesn't exist, the exception raised should be safe to call
317 321 str() on - i.e. in Python 2 it must only have ASCII characters.
318 322
319 323 https://github.com/ipython/ipython/issues/875
320 324 """
321 325 try:
322 326 # these calls should not throw unicode encode exceptions
323 327 path.get_py_filename('fooéè.py')
324 328 except IOError as ex:
325 329 str(ex)
326 330
327 331
328 332 class TestShellGlob(unittest.TestCase):
329 333
330 334 @classmethod
331 335 def setUpClass(cls):
332 336 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
333 337 cls.filenames_end_with_b = ['0b', '1b', '2b']
334 338 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
335 339 cls.tempdir = TemporaryDirectory()
336 340 td = cls.tempdir.name
337 341
338 342 with cls.in_tempdir():
339 343 # Create empty files
340 344 for fname in cls.filenames:
341 345 open(os.path.join(td, fname), 'w').close()
342 346
343 347 @classmethod
344 348 def tearDownClass(cls):
345 349 cls.tempdir.cleanup()
346 350
347 351 @classmethod
348 352 @contextmanager
349 353 def in_tempdir(cls):
350 354 save = os.getcwd()
351 355 try:
352 356 os.chdir(cls.tempdir.name)
353 357 yield
354 358 finally:
355 359 os.chdir(save)
356 360
357 361 def check_match(self, patterns, matches):
358 362 with self.in_tempdir():
359 363 # glob returns unordered list. that's why sorted is required.
360 364 nt.assert_equal(sorted(path.shellglob(patterns)),
361 365 sorted(matches))
362 366
363 367 def common_cases(self):
364 368 return [
365 369 (['*'], self.filenames),
366 370 (['a*'], self.filenames_start_with_a),
367 371 (['*c'], ['*c']),
368 372 (['*', 'a*', '*b', '*c'], self.filenames
369 373 + self.filenames_start_with_a
370 374 + self.filenames_end_with_b
371 375 + ['*c']),
372 376 (['a[012]'], self.filenames_start_with_a),
373 377 ]
374 378
375 379 @skip_win32
376 380 def test_match_posix(self):
377 381 for (patterns, matches) in self.common_cases() + [
378 382 ([r'\*'], ['*']),
379 383 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
380 384 ([r'a\[012]'], ['a[012]']),
381 385 ]:
382 386 yield (self.check_match, patterns, matches)
383 387
384 388 @skip_if_not_win32
385 389 def test_match_windows(self):
386 390 for (patterns, matches) in self.common_cases() + [
387 391 # In windows, backslash is interpreted as path
388 392 # separator. Therefore, you can't escape glob
389 393 # using it.
390 394 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
391 395 ([r'a\[012]'], [r'a\[012]']),
392 396 ]:
393 397 yield (self.check_match, patterns, matches)
394 398
395 399
396 400 def test_unescape_glob():
397 401 nt.assert_equal(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
398 402 nt.assert_equal(path.unescape_glob(r'\\*'), r'\*')
399 403 nt.assert_equal(path.unescape_glob(r'\\\*'), r'\*')
400 404 nt.assert_equal(path.unescape_glob(r'\\a'), r'\a')
401 405 nt.assert_equal(path.unescape_glob(r'\a'), r'\a')
402 406
403 407
404 408 @onlyif_unicode_paths
405 409 def test_ensure_dir_exists():
406 410 with TemporaryDirectory() as td:
407 411 d = os.path.join(td, 'βˆ‚ir')
408 412 path.ensure_dir_exists(d) # create it
409 413 assert os.path.isdir(d)
410 414 path.ensure_dir_exists(d) # no-op
411 415 f = os.path.join(td, 'Ζ’ile')
412 416 open(f, 'w').close() # touch
413 417 with nt.assert_raises(IOError):
414 418 path.ensure_dir_exists(f)
415 419
416 420 class TestLinkOrCopy(unittest.TestCase):
417 421 def setUp(self):
418 422 self.tempdir = TemporaryDirectory()
419 423 self.src = self.dst("src")
420 424 with open(self.src, "w") as f:
421 425 f.write("Hello, world!")
422 426
423 427 def tearDown(self):
424 428 self.tempdir.cleanup()
425 429
426 430 def dst(self, *args):
427 431 return os.path.join(self.tempdir.name, *args)
428 432
429 433 def assert_inode_not_equal(self, a, b):
430 434 nt.assert_not_equal(os.stat(a).st_ino, os.stat(b).st_ino,
431 435 "%r and %r do reference the same indoes" %(a, b))
432 436
433 437 def assert_inode_equal(self, a, b):
434 438 nt.assert_equal(os.stat(a).st_ino, os.stat(b).st_ino,
435 439 "%r and %r do not reference the same indoes" %(a, b))
436 440
437 441 def assert_content_equal(self, a, b):
438 442 with open(a) as a_f:
439 443 with open(b) as b_f:
440 444 nt.assert_equal(a_f.read(), b_f.read())
441 445
442 446 @skip_win32
443 447 def test_link_successful(self):
444 448 dst = self.dst("target")
445 449 path.link_or_copy(self.src, dst)
446 450 self.assert_inode_equal(self.src, dst)
447 451
448 452 @skip_win32
449 453 def test_link_into_dir(self):
450 454 dst = self.dst("some_dir")
451 455 os.mkdir(dst)
452 456 path.link_or_copy(self.src, dst)
453 457 expected_dst = self.dst("some_dir", os.path.basename(self.src))
454 458 self.assert_inode_equal(self.src, expected_dst)
455 459
456 460 @skip_win32
457 461 def test_target_exists(self):
458 462 dst = self.dst("target")
459 463 open(dst, "w").close()
460 464 path.link_or_copy(self.src, dst)
461 465 self.assert_inode_equal(self.src, dst)
462 466
463 467 @skip_win32
464 468 def test_no_link(self):
465 469 real_link = os.link
466 470 try:
467 471 del os.link
468 472 dst = self.dst("target")
469 473 path.link_or_copy(self.src, dst)
470 474 self.assert_content_equal(self.src, dst)
471 475 self.assert_inode_not_equal(self.src, dst)
472 476 finally:
473 477 os.link = real_link
474 478
475 479 @skip_if_not_win32
476 480 def test_windows(self):
477 481 dst = self.dst("target")
478 482 path.link_or_copy(self.src, dst)
479 483 self.assert_content_equal(self.src, dst)
480 484
481 485 def test_link_twice(self):
482 486 # Linking the same file twice shouldn't leave duplicates around.
483 487 # See https://github.com/ipython/ipython/issues/6450
484 488 dst = self.dst('target')
485 489 path.link_or_copy(self.src, dst)
486 490 path.link_or_copy(self.src, dst)
487 491 self.assert_inode_equal(self.src, dst)
488 492 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