##// END OF EJS Templates
IPython/utils/tests/test_path.py: Only use `encode` for system functions that cannot handle unicode.
John Kirkham -
Show More
@@ -1,514 +1,514
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 errno
8 8 import os
9 9 import shutil
10 10 import sys
11 11 import tempfile
12 12 import warnings
13 13 from contextlib import contextmanager
14 14
15 15 try: # Python 3.3+
16 16 from unittest.mock import patch
17 17 except ImportError:
18 18 from mock import patch
19 19
20 20 from os.path import join, abspath, split
21 21
22 22 from nose import SkipTest
23 23 import nose.tools as nt
24 24
25 25 from nose import with_setup
26 26
27 27 import IPython
28 28 from IPython import paths
29 29 from IPython.testing import decorators as dec
30 30 from IPython.testing.decorators import (skip_if_not_win32, skip_win32,
31 31 onlyif_unicode_paths,)
32 32 from IPython.testing.tools import make_tempfile, AssertPrints
33 33 from IPython.utils import path
34 34 from IPython.utils import py3compat
35 35 from IPython.utils.tempdir import TemporaryDirectory
36 36
37 37 # Platform-dependent imports
38 38 try:
39 39 import winreg as wreg # Py 3
40 40 except ImportError:
41 41 try:
42 42 import _winreg as wreg # Py 2
43 43 except ImportError:
44 44 #Fake _winreg module on none windows platforms
45 45 import types
46 46 wr_name = "winreg" if py3compat.PY3 else "_winreg"
47 47 sys.modules[wr_name] = types.ModuleType(wr_name)
48 48 try:
49 49 import winreg as wreg
50 50 except ImportError:
51 51 import _winreg as wreg
52 52 #Add entries that needs to be stubbed by the testing code
53 53 (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
54 54
55 55 try:
56 56 reload
57 57 except NameError: # Python 3
58 58 from imp import reload
59 59
60 60 #-----------------------------------------------------------------------------
61 61 # Globals
62 62 #-----------------------------------------------------------------------------
63 63 env = os.environ
64 64 TMP_TEST_DIR = tempfile.mkdtemp()
65 65 HOME_TEST_DIR = join(TMP_TEST_DIR, "home_test_dir")
66 66 #
67 67 # Setup/teardown functions/decorators
68 68 #
69 69
70 70 def setup():
71 71 """Setup testenvironment for the module:
72 72
73 73 - Adds dummy home dir tree
74 74 """
75 75 # Do not mask exceptions here. In particular, catching WindowsError is a
76 76 # problem because that exception is only defined on Windows...
77 77 os.makedirs(os.path.join(HOME_TEST_DIR, 'ipython'))
78 78
79 79
80 80 def teardown():
81 81 """Teardown testenvironment for the module:
82 82
83 83 - Remove dummy home dir tree
84 84 """
85 85 # Note: we remove the parent test dir, which is the root of all test
86 86 # subdirs we may have created. Use shutil instead of os.removedirs, so
87 87 # that non-empty directories are all recursively removed.
88 88 shutil.rmtree(TMP_TEST_DIR)
89 89
90 90
91 91 def setup_environment():
92 92 """Setup testenvironment for some functions that are tested
93 93 in this module. In particular this functions stores attributes
94 94 and other things that we need to stub in some test functions.
95 95 This needs to be done on a function level and not module level because
96 96 each testfunction needs a pristine environment.
97 97 """
98 98 global oldstuff, platformstuff
99 99 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
100 100
101 101 def teardown_environment():
102 102 """Restore things that were remembered by the setup_environment function
103 103 """
104 104 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
105 105 os.chdir(old_wd)
106 106 reload(path)
107 107
108 108 for key in list(env):
109 109 if key not in oldenv:
110 110 del env[key]
111 111 env.update(oldenv)
112 112 if hasattr(sys, 'frozen'):
113 113 del sys.frozen
114 114
115 115 # Build decorator that uses the setup_environment/setup_environment
116 116 with_environment = with_setup(setup_environment, teardown_environment)
117 117
118 118 @skip_if_not_win32
119 119 @with_environment
120 120 def test_get_home_dir_1():
121 121 """Testcase for py2exe logic, un-compressed lib
122 122 """
123 123 unfrozen = path.get_home_dir()
124 124 sys.frozen = True
125 125
126 126 #fake filename for IPython.__init__
127 127 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Lib/IPython/__init__.py"))
128 128
129 129 home_dir = path.get_home_dir()
130 130 nt.assert_equal(home_dir, unfrozen)
131 131
132 132
133 133 @skip_if_not_win32
134 134 @with_environment
135 135 def test_get_home_dir_2():
136 136 """Testcase for py2exe logic, compressed lib
137 137 """
138 138 unfrozen = path.get_home_dir()
139 139 sys.frozen = True
140 140 #fake filename for IPython.__init__
141 141 IPython.__file__ = abspath(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
142 142
143 143 home_dir = path.get_home_dir(True)
144 144 nt.assert_equal(home_dir, unfrozen)
145 145
146 146
147 147 @with_environment
148 148 def test_get_home_dir_3():
149 149 """get_home_dir() uses $HOME if set"""
150 150 env["HOME"] = HOME_TEST_DIR
151 151 home_dir = path.get_home_dir(True)
152 152 # get_home_dir expands symlinks
153 153 nt.assert_equal(home_dir, os.path.realpath(env["HOME"]))
154 154
155 155
156 156 @with_environment
157 157 def test_get_home_dir_4():
158 158 """get_home_dir() still works if $HOME is not set"""
159 159
160 160 if 'HOME' in env: del env['HOME']
161 161 # this should still succeed, but we don't care what the answer is
162 162 home = path.get_home_dir(False)
163 163
164 164 @with_environment
165 165 def test_get_home_dir_5():
166 166 """raise HomeDirError if $HOME is specified, but not a writable dir"""
167 167 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
168 168 # set os.name = posix, to prevent My Documents fallback on Windows
169 169 os.name = 'posix'
170 170 nt.assert_raises(path.HomeDirError, path.get_home_dir, True)
171 171
172 172 # Should we stub wreg fully so we can run the test on all platforms?
173 173 @skip_if_not_win32
174 174 @with_environment
175 175 def test_get_home_dir_8():
176 176 """Using registry hack for 'My Documents', os=='nt'
177 177
178 178 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
179 179 """
180 180 os.name = 'nt'
181 181 # Remove from stub environment all keys that may be set
182 182 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
183 183 env.pop(key, None)
184 184
185 185 class key:
186 186 def Close(self):
187 187 pass
188 188
189 189 with patch.object(wreg, 'OpenKey', return_value=key()), \
190 190 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
191 191 home_dir = path.get_home_dir()
192 192 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
193 193
194 194 @with_environment
195 195 def test_get_xdg_dir_0():
196 196 """test_get_xdg_dir_0, check xdg_dir"""
197 197 reload(path)
198 198 path._writable_dir = lambda path: True
199 199 path.get_home_dir = lambda : 'somewhere'
200 200 os.name = "posix"
201 201 sys.platform = "linux2"
202 202 env.pop('IPYTHON_DIR', None)
203 203 env.pop('IPYTHONDIR', None)
204 204 env.pop('XDG_CONFIG_HOME', None)
205 205
206 206 nt.assert_equal(path.get_xdg_dir(), os.path.join('somewhere', '.config'))
207 207
208 208
209 209 @with_environment
210 210 def test_get_xdg_dir_1():
211 211 """test_get_xdg_dir_1, check nonexistant xdg_dir"""
212 212 reload(path)
213 213 path.get_home_dir = lambda : HOME_TEST_DIR
214 214 os.name = "posix"
215 215 sys.platform = "linux2"
216 216 env.pop('IPYTHON_DIR', None)
217 217 env.pop('IPYTHONDIR', None)
218 218 env.pop('XDG_CONFIG_HOME', None)
219 219 nt.assert_equal(path.get_xdg_dir(), None)
220 220
221 221 @with_environment
222 222 def test_get_xdg_dir_2():
223 223 """test_get_xdg_dir_2, check xdg_dir default to ~/.config"""
224 224 reload(path)
225 225 path.get_home_dir = lambda : HOME_TEST_DIR
226 226 os.name = "posix"
227 227 sys.platform = "linux2"
228 228 env.pop('IPYTHON_DIR', None)
229 229 env.pop('IPYTHONDIR', None)
230 230 env.pop('XDG_CONFIG_HOME', None)
231 231 cfgdir=os.path.join(path.get_home_dir(), '.config')
232 232 if not os.path.exists(cfgdir):
233 233 os.makedirs(cfgdir)
234 234
235 235 nt.assert_equal(path.get_xdg_dir(), cfgdir)
236 236
237 237 @with_environment
238 238 def test_get_xdg_dir_3():
239 239 """test_get_xdg_dir_3, check xdg_dir not used on OS X"""
240 240 reload(path)
241 241 path.get_home_dir = lambda : HOME_TEST_DIR
242 242 os.name = "posix"
243 243 sys.platform = "darwin"
244 244 env.pop('IPYTHON_DIR', None)
245 245 env.pop('IPYTHONDIR', None)
246 246 env.pop('XDG_CONFIG_HOME', None)
247 247 cfgdir=os.path.join(path.get_home_dir(), '.config')
248 248 if not os.path.exists(cfgdir):
249 249 os.makedirs(cfgdir)
250 250
251 251 nt.assert_equal(path.get_xdg_dir(), None)
252 252
253 253 def test_filefind():
254 254 """Various tests for filefind"""
255 255 f = tempfile.NamedTemporaryFile()
256 256 # print 'fname:',f.name
257 257 alt_dirs = paths.get_ipython_dir()
258 258 t = path.filefind(f.name, alt_dirs)
259 259 # print 'found:',t
260 260
261 261
262 262 @dec.skip_if_not_win32
263 263 def test_get_long_path_name_win32():
264 264 with TemporaryDirectory() as tmpdir:
265 265
266 266 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
267 267 # path component, so ensure we include the long form of it
268 268 long_path = os.path.join(path.get_long_path_name(tmpdir), u'this is my long path name')
269 269 os.makedirs(long_path)
270 270
271 271 # Test to see if the short path evaluates correctly.
272 272 short_path = os.path.join(tmpdir, u'THISIS~1')
273 273 evaluated_path = path.get_long_path_name(short_path)
274 274 nt.assert_equal(evaluated_path.lower(), long_path.lower())
275 275
276 276
277 277 @dec.skip_win32
278 278 def test_get_long_path_name():
279 279 p = path.get_long_path_name('/usr/local')
280 280 nt.assert_equal(p,'/usr/local')
281 281
282 282 @dec.skip_win32 # can't create not-user-writable dir on win
283 283 @with_environment
284 284 def test_not_writable_ipdir():
285 285 tmpdir = tempfile.mkdtemp()
286 286 os.name = "posix"
287 287 env.pop('IPYTHON_DIR', None)
288 288 env.pop('IPYTHONDIR', None)
289 289 env.pop('XDG_CONFIG_HOME', None)
290 290 env['HOME'] = tmpdir
291 291 ipdir = os.path.join(tmpdir, '.ipython')
292 292 os.mkdir(ipdir, 0o555)
293 293 try:
294 294 open(os.path.join(ipdir, "_foo_"), 'w').close()
295 295 except IOError:
296 296 pass
297 297 else:
298 298 # I can still write to an unwritable dir,
299 299 # assume I'm root and skip the test
300 300 raise SkipTest("I can't create directories that I can't write to")
301 301 with AssertPrints('is not a writable location', channel='stderr'):
302 302 ipdir = paths.get_ipython_dir()
303 303 env.pop('IPYTHON_DIR', None)
304 304
305 305 def test_unquote_filename():
306 306 for win32 in (True, False):
307 307 nt.assert_equal(path.unquote_filename('foo.py', win32=win32), 'foo.py')
308 308 nt.assert_equal(path.unquote_filename('foo bar.py', win32=win32), 'foo bar.py')
309 309 nt.assert_equal(path.unquote_filename('"foo.py"', win32=True), 'foo.py')
310 310 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=True), 'foo bar.py')
311 311 nt.assert_equal(path.unquote_filename("'foo.py'", win32=True), 'foo.py')
312 312 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=True), 'foo bar.py')
313 313 nt.assert_equal(path.unquote_filename('"foo.py"', win32=False), '"foo.py"')
314 314 nt.assert_equal(path.unquote_filename('"foo bar.py"', win32=False), '"foo bar.py"')
315 315 nt.assert_equal(path.unquote_filename("'foo.py'", win32=False), "'foo.py'")
316 316 nt.assert_equal(path.unquote_filename("'foo bar.py'", win32=False), "'foo bar.py'")
317 317
318 318 @with_environment
319 319 def test_get_py_filename():
320 320 os.chdir(TMP_TEST_DIR)
321 321 for win32 in (True, False):
322 322 with make_tempfile('foo.py'):
323 323 nt.assert_equal(path.get_py_filename('foo.py', force_win32=win32), 'foo.py')
324 324 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo.py')
325 325 with make_tempfile('foo'):
326 326 nt.assert_equal(path.get_py_filename('foo', force_win32=win32), 'foo')
327 327 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
328 328 nt.assert_raises(IOError, path.get_py_filename, 'foo', force_win32=win32)
329 329 nt.assert_raises(IOError, path.get_py_filename, 'foo.py', force_win32=win32)
330 330 true_fn = 'foo with spaces.py'
331 331 with make_tempfile(true_fn):
332 332 nt.assert_equal(path.get_py_filename('foo with spaces', force_win32=win32), true_fn)
333 333 nt.assert_equal(path.get_py_filename('foo with spaces.py', force_win32=win32), true_fn)
334 334 if win32:
335 335 nt.assert_equal(path.get_py_filename('"foo with spaces.py"', force_win32=True), true_fn)
336 336 nt.assert_equal(path.get_py_filename("'foo with spaces.py'", force_win32=True), true_fn)
337 337 else:
338 338 nt.assert_raises(IOError, path.get_py_filename, '"foo with spaces.py"', force_win32=False)
339 339 nt.assert_raises(IOError, path.get_py_filename, "'foo with spaces.py'", force_win32=False)
340 340
341 341 @onlyif_unicode_paths
342 342 def test_unicode_in_filename():
343 343 """When a file doesn't exist, the exception raised should be safe to call
344 344 str() on - i.e. in Python 2 it must only have ASCII characters.
345 345
346 346 https://github.com/ipython/ipython/issues/875
347 347 """
348 348 try:
349 349 # these calls should not throw unicode encode exceptions
350 350 path.get_py_filename(u'fooéè.py', force_win32=False)
351 351 except IOError as ex:
352 352 str(ex)
353 353
354 354
355 355 class TestShellGlob(object):
356 356
357 357 @classmethod
358 358 def setUpClass(cls):
359 359 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
360 360 cls.filenames_end_with_b = ['0b', '1b', '2b']
361 361 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
362 362 cls.tempdir = TemporaryDirectory()
363 363 td = cls.tempdir.name
364 364
365 365 with cls.in_tempdir():
366 366 # Create empty files
367 367 for fname in cls.filenames:
368 368 open(os.path.join(td, fname), 'w').close()
369 369
370 370 @classmethod
371 371 def tearDownClass(cls):
372 372 cls.tempdir.cleanup()
373 373
374 374 @classmethod
375 375 @contextmanager
376 376 def in_tempdir(cls):
377 377 save = py3compat.getcwd()
378 378 try:
379 379 os.chdir(cls.tempdir.name)
380 380 yield
381 381 finally:
382 382 os.chdir(save)
383 383
384 384 def check_match(self, patterns, matches):
385 385 with self.in_tempdir():
386 386 # glob returns unordered list. that's why sorted is required.
387 387 nt.assert_equals(sorted(path.shellglob(patterns)),
388 388 sorted(matches))
389 389
390 390 def common_cases(self):
391 391 return [
392 392 (['*'], self.filenames),
393 393 (['a*'], self.filenames_start_with_a),
394 394 (['*c'], ['*c']),
395 395 (['*', 'a*', '*b', '*c'], self.filenames
396 396 + self.filenames_start_with_a
397 397 + self.filenames_end_with_b
398 398 + ['*c']),
399 399 (['a[012]'], self.filenames_start_with_a),
400 400 ]
401 401
402 402 @skip_win32
403 403 def test_match_posix(self):
404 404 for (patterns, matches) in self.common_cases() + [
405 405 ([r'\*'], ['*']),
406 406 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
407 407 ([r'a\[012]'], ['a[012]']),
408 408 ]:
409 409 yield (self.check_match, patterns, matches)
410 410
411 411 @skip_if_not_win32
412 412 def test_match_windows(self):
413 413 for (patterns, matches) in self.common_cases() + [
414 414 # In windows, backslash is interpreted as path
415 415 # separator. Therefore, you can't escape glob
416 416 # using it.
417 417 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
418 418 ([r'a\[012]'], [r'a\[012]']),
419 419 ]:
420 420 yield (self.check_match, patterns, matches)
421 421
422 422
423 423 def test_unescape_glob():
424 424 nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?')
425 425 nt.assert_equals(path.unescape_glob(r'\\*'), r'\*')
426 426 nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*')
427 427 nt.assert_equals(path.unescape_glob(r'\\a'), r'\a')
428 428 nt.assert_equals(path.unescape_glob(r'\a'), r'\a')
429 429
430 430
431 431 def test_ensure_dir_exists():
432 432 with TemporaryDirectory() as td:
433 d = os.path.join(td, u'βˆ‚ir').encode("utf8")
433 d = os.path.join(td, u'βˆ‚ir')
434 434 path.ensure_dir_exists(d) # create it
435 assert os.path.isdir(d)
435 assert os.path.isdir(d.encode("utf8"))
436 436 path.ensure_dir_exists(d) # no-op
437 f = os.path.join(td, u'Ζ’ile').encode("utf8")
438 open(f, 'w').close() # touch
437 f = os.path.join(td, u'Ζ’ile')
438 open(f.encode("utf8"), 'w').close() # touch
439 439 with nt.assert_raises(IOError):
440 440 path.ensure_dir_exists(f)
441 441
442 442 class TestLinkOrCopy(object):
443 443 def setUp(self):
444 444 self.tempdir = TemporaryDirectory()
445 445 self.src = self.dst("src")
446 446 with open(self.src, "w") as f:
447 447 f.write("Hello, world!")
448 448
449 449 def tearDown(self):
450 450 self.tempdir.cleanup()
451 451
452 452 def dst(self, *args):
453 453 return os.path.join(self.tempdir.name, *args)
454 454
455 455 def assert_inode_not_equal(self, a, b):
456 456 nt.assert_not_equals(os.stat(a).st_ino, os.stat(b).st_ino,
457 457 "%r and %r do reference the same indoes" %(a, b))
458 458
459 459 def assert_inode_equal(self, a, b):
460 460 nt.assert_equals(os.stat(a).st_ino, os.stat(b).st_ino,
461 461 "%r and %r do not reference the same indoes" %(a, b))
462 462
463 463 def assert_content_equal(self, a, b):
464 464 with open(a) as a_f:
465 465 with open(b) as b_f:
466 466 nt.assert_equals(a_f.read(), b_f.read())
467 467
468 468 @skip_win32
469 469 def test_link_successful(self):
470 470 dst = self.dst("target")
471 471 path.link_or_copy(self.src, dst)
472 472 self.assert_inode_equal(self.src, dst)
473 473
474 474 @skip_win32
475 475 def test_link_into_dir(self):
476 476 dst = self.dst("some_dir")
477 477 os.mkdir(dst)
478 478 path.link_or_copy(self.src, dst)
479 479 expected_dst = self.dst("some_dir", os.path.basename(self.src))
480 480 self.assert_inode_equal(self.src, expected_dst)
481 481
482 482 @skip_win32
483 483 def test_target_exists(self):
484 484 dst = self.dst("target")
485 485 open(dst, "w").close()
486 486 path.link_or_copy(self.src, dst)
487 487 self.assert_inode_equal(self.src, dst)
488 488
489 489 @skip_win32
490 490 def test_no_link(self):
491 491 real_link = os.link
492 492 try:
493 493 del os.link
494 494 dst = self.dst("target")
495 495 path.link_or_copy(self.src, dst)
496 496 self.assert_content_equal(self.src, dst)
497 497 self.assert_inode_not_equal(self.src, dst)
498 498 finally:
499 499 os.link = real_link
500 500
501 501 @skip_if_not_win32
502 502 def test_windows(self):
503 503 dst = self.dst("target")
504 504 path.link_or_copy(self.src, dst)
505 505 self.assert_content_equal(self.src, dst)
506 506
507 507 def test_link_twice(self):
508 508 # Linking the same file twice shouldn't leave duplicates around.
509 509 # See https://github.com/ipython/ipython/issues/6450
510 510 dst = self.dst('target')
511 511 path.link_or_copy(self.src, dst)
512 512 path.link_or_copy(self.src, dst)
513 513 self.assert_inode_equal(self.src, dst)
514 514 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