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