##// END OF EJS Templates
Merge branch 'main' into use-conda-env-exe
Shaun Walbridge -
r28858:c6ed7871 merge
parent child Browse files
Show More
@@ -1,71 +1,73
1 name: Run Downstream tests
1 name: Run Downstream tests
2
2
3 on:
3 on:
4 push:
4 push:
5 pull_request:
5 pull_request:
6 # Run weekly on Monday at 1:23 UTC
6 # Run weekly on Monday at 1:23 UTC
7 schedule:
7 schedule:
8 - cron: '23 1 * * 1'
8 - cron: '23 1 * * 1'
9 workflow_dispatch:
9 workflow_dispatch:
10
10
11 permissions:
11 permissions:
12 contents: read
12 contents: read
13
13
14 jobs:
14 jobs:
15 test:
15 test:
16 runs-on: ${{ matrix.os }}
16 runs-on: ${{ matrix.os }}
17 # Disable scheduled CI runs on forks
17 # Disable scheduled CI runs on forks
18 if: github.event_name != 'schedule' || github.repository_owner == 'ipython'
18 if: github.event_name != 'schedule' || github.repository_owner == 'ipython'
19 strategy:
19 strategy:
20 matrix:
20 matrix:
21 os: [ubuntu-latest]
21 os: [ubuntu-latest]
22 python-version: ["3.10"]
22 python-version: ["3.10"]
23 include:
23 include:
24 - os: macos-13
24 - os: macos-13
25 python-version: "3.10"
25 python-version: "3.10"
26
26
27 steps:
27 steps:
28 - uses: actions/checkout@v3
28 - uses: actions/checkout@v3
29 - name: Set up Python ${{ matrix.python-version }}
29 - name: Set up Python ${{ matrix.python-version }}
30 uses: actions/setup-python@v4
30 uses: actions/setup-python@v4
31 with:
31 with:
32 python-version: ${{ matrix.python-version }}
32 python-version: ${{ matrix.python-version }}
33 - name: Update Python installer
33 - name: Update Python installer
34 run: |
34 run: |
35 python -m pip install --upgrade pip setuptools wheel
35 python -m pip install --upgrade pip setuptools wheel
36 - name: Install ipykernel
36 - name: Install ipykernel
37 run: |
37 run: |
38 cd ..
38 cd ..
39 git clone https://github.com/ipython/ipykernel
39 git clone https://github.com/ipython/ipykernel
40 cd ipykernel
40 cd ipykernel
41 pip install -e .[test]
41 pip install -e .[test]
42 cd ..
42 cd ..
43 - name: Install and update Python dependencies
43 - name: Install and update Python dependencies
44 run: |
44 run: |
45 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
45 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
46 # we must install IPython after ipykernel to get the right versions.
46 # we must install IPython after ipykernel to get the right versions.
47 python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel
47 python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel
48 - name: pytest ipykernel
48 - name: pytest ipykernel
49 env:
49 env:
50 COLUMNS: 120
50 COLUMNS: 120
51 run: |
51 run: |
52 cd ../ipykernel
52 cd ../ipykernel
53 pytest
53 pytest
54 - name: Install sagemath-repl
54 - name: Install sagemath-repl
55 run: |
55 run: |
56 cd ..
56 # Sept 2024, sage has been failing for a while,
57 git clone --depth 1 https://github.com/sagemath/sage
57 # Skipping.
58 cd sage
58 # cd ..
59 # We cloned it for the tests, but for simplicity we install the
59 # git clone --depth 1 https://github.com/sagemath/sage
60 # wheels from PyPI.
60 # cd sage
61 # (Avoid 10.3b6 because of https://github.com/sagemath/sage/pull/37178)
61 # # We cloned it for the tests, but for simplicity we install the
62 pip install --pre sagemath-repl sagemath-environment
62 # # wheels from PyPI.
63 # Install optionals that make more tests pass
63 # # (Avoid 10.3b6 because of https://github.com/sagemath/sage/pull/37178)
64 pip install pillow
64 # pip install --pre sagemath-repl sagemath-environment
65 pip install --pre sagemath-categories
65 # # Install optionals that make more tests pass
66 cd ..
66 # pip install pillow
67 # pip install --pre sagemath-categories
68 # cd ..
67 - name: Test sagemath-repl
69 - name: Test sagemath-repl
68 run: |
70 run: |
69 cd ../sage/
71 # cd ../sage/
70 # From https://github.com/sagemath/sage/blob/develop/pkgs/sagemath-repl/tox.ini
72 # # From https://github.com/sagemath/sage/blob/develop/pkgs/sagemath-repl/tox.ini
71 sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path=pkgs/sagemath-repl/known-test-failures.json --initial --optional=sage src/sage/repl src/sage/doctest src/sage/misc/sage_input.py src/sage/misc/sage_eval.py
73 # sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path=pkgs/sagemath-repl/known-test-failures.json --initial --optional=sage src/sage/repl src/sage/doctest src/sage/misc/sage_input.py src/sage/misc/sage_eval.py
@@ -1,509 +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():
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 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():
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 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(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
137 IPython.__file__ = abspath(
138 join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")
139 ).lower()
138
140
139 home_dir = path.get_home_dir(True)
141 home_dir = path.get_home_dir(True)
140 assert home_dir == unfrozen
142 assert home_dir == unfrozen
141
143
142
144
143 @skip_win32
145 @skip_win32
144 @with_environment
146 @with_environment
145 def test_get_home_dir_3():
147 def test_get_home_dir_3():
146 """get_home_dir() uses $HOME if set"""
148 """get_home_dir() uses $HOME if set"""
147 env["HOME"] = HOME_TEST_DIR
149 env["HOME"] = HOME_TEST_DIR
148 home_dir = path.get_home_dir(True)
150 home_dir = path.get_home_dir(True)
149 # get_home_dir expands symlinks
151 # get_home_dir expands symlinks
150 assert home_dir == os.path.realpath(env["HOME"])
152 assert home_dir == os.path.realpath(env["HOME"])
151
153
152
154
153 @with_environment
155 @with_environment
154 def test_get_home_dir_4():
156 def test_get_home_dir_4():
155 """get_home_dir() still works if $HOME is not set"""
157 """get_home_dir() still works if $HOME is not set"""
156
158
157 if 'HOME' in env: del env['HOME']
159 if 'HOME' in env: del env['HOME']
158 # 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
159 home = path.get_home_dir(False)
161 home = path.get_home_dir(False)
160
162
161 @skip_win32
163 @skip_win32
162 @with_environment
164 @with_environment
163 def test_get_home_dir_5():
165 def test_get_home_dir_5(monkeypatch):
164 """raise HomeDirError if $HOME is specified, but not a writable dir"""
166 """raise HomeDirError if $HOME is specified, but not a writable dir"""
165 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
167 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
166 # set os.name = posix, to prevent My Documents fallback on Windows
168 # set os.name = posix, to prevent My Documents fallback on Windows
167 os.name = 'posix'
169 monkeypatch.setattr(os, "name", "posix")
168 pytest.raises(path.HomeDirError, path.get_home_dir, True)
170 pytest.raises(path.HomeDirError, path.get_home_dir, True)
169
171
170 # 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?
171 @skip_if_not_win32
173 @skip_if_not_win32
172 @with_environment
174 @with_environment
173 def test_get_home_dir_8():
175 def test_get_home_dir_8(monkeypatch):
174 """Using registry hack for 'My Documents', os=='nt'
176 """Using registry hack for 'My Documents', os=='nt'
175
177
176 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
178 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
177 """
179 """
178 os.name = 'nt'
180 monkeypatch.setattr(os, "name", "nt")
179 # Remove from stub environment all keys that may be set
181 # Remove from stub environment all keys that may be set
180 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
182 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
181 env.pop(key, None)
183 env.pop(key, None)
182
184
183 class key:
185 class key:
184 def __enter__(self):
186 def __enter__(self):
185 pass
187 pass
186 def Close(self):
188 def Close(self):
187 pass
189 pass
188 def __exit__(*args, **kwargs):
190 def __exit__(*args, **kwargs):
189 pass
191 pass
190
192
191 with patch.object(wreg, 'OpenKey', return_value=key()), \
193 with patch.object(wreg, 'OpenKey', return_value=key()), \
192 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
194 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
193 home_dir = path.get_home_dir()
195 home_dir = path.get_home_dir()
194 assert home_dir == abspath(HOME_TEST_DIR)
196 assert home_dir == abspath(HOME_TEST_DIR)
195
197
196 @with_environment
198 @with_environment
197 def test_get_xdg_dir_0():
199 def test_get_xdg_dir_0(monkeypatch):
198 """test_get_xdg_dir_0, check xdg_dir"""
200 """test_get_xdg_dir_0, check xdg_dir"""
199 reload(path)
201 monkeypatch.setattr(path, "_writable_dir", lambda path: True)
200 path._writable_dir = lambda path: True
202 monkeypatch.setattr(path, "get_home_dir", lambda: "somewhere")
201 path.get_home_dir = lambda : 'somewhere'
203 monkeypatch.setattr(os, "name", "posix")
202 os.name = "posix"
204 monkeypatch.setattr(sys, "platform", "linux2")
203 sys.platform = "linux2"
204 env.pop('IPYTHON_DIR', None)
205 env.pop('IPYTHON_DIR', None)
205 env.pop('IPYTHONDIR', None)
206 env.pop('IPYTHONDIR', None)
206 env.pop('XDG_CONFIG_HOME', None)
207 env.pop('XDG_CONFIG_HOME', None)
207
208
208 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
209 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
209
210
210
211
211 @with_environment
212 @with_environment
212 def test_get_xdg_dir_1():
213 def test_get_xdg_dir_1(monkeypatch):
213 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
214 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
214 reload(path)
215 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
215 path.get_home_dir = lambda : HOME_TEST_DIR
216 monkeypatch.setattr(os, "name", "posix")
216 os.name = "posix"
217 monkeypatch.setattr(sys, "platform", "linux2")
217 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():
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 reload(path)
226 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
227 path.get_home_dir = lambda : HOME_TEST_DIR
227 monkeypatch.setattr(os, "name", "posix")
228 os.name = "posix"
228 monkeypatch.setattr(sys, "platform", "linux2")
229 sys.platform = "linux2"
229 env.pop("IPYTHON_DIR", None)
230 env.pop('IPYTHON_DIR', None)
230 env.pop("IPYTHONDIR", None)
231 env.pop('IPYTHONDIR', None)
231 env.pop("XDG_CONFIG_HOME", None)
232 env.pop('XDG_CONFIG_HOME', None)
232 cfgdir = os.path.join(path.get_home_dir(), ".config")
233 cfgdir=os.path.join(path.get_home_dir(), '.config')
234 if not os.path.exists(cfgdir):
233 if not os.path.exists(cfgdir):
235 os.makedirs(cfgdir)
234 os.makedirs(cfgdir)
236
235
237 assert path.get_xdg_dir() == cfgdir
236 assert path.get_xdg_dir() == cfgdir
238
237
239 @with_environment
238 @with_environment
240 def test_get_xdg_dir_3():
239 def test_get_xdg_dir_3(monkeypatch):
241 """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"""
242 reload(path)
241 monkeypatch.setattr(path, "get_home_dir", lambda: HOME_TEST_DIR)
243 path.get_home_dir = lambda : HOME_TEST_DIR
242 monkeypatch.setattr(os, "name", "nt")
244 os.name = "nt"
243 monkeypatch.setattr(sys, "platform", "win32")
245 sys.platform = "win32"
244 env.pop("IPYTHON_DIR", None)
246 env.pop('IPYTHON_DIR', None)
245 env.pop("IPYTHONDIR", None)
247 env.pop('IPYTHONDIR', None)
246 env.pop("XDG_CONFIG_HOME", None)
248 env.pop('XDG_CONFIG_HOME', None)
247 cfgdir = os.path.join(path.get_home_dir(), ".config")
249 cfgdir=os.path.join(path.get_home_dir(), '.config')
250 os.makedirs(cfgdir, exist_ok=True)
248 os.makedirs(cfgdir, exist_ok=True)
251
249
252 assert path.get_xdg_dir() is None
250 assert path.get_xdg_dir() is None
253
251
254 def test_filefind():
252 def test_filefind():
255 """Various tests for filefind"""
253 """Various tests for filefind"""
256 f = tempfile.NamedTemporaryFile()
254 f = tempfile.NamedTemporaryFile()
257 # print('fname:',f.name)
255 # print('fname:',f.name)
258 alt_dirs = paths.get_ipython_dir()
256 alt_dirs = paths.get_ipython_dir()
259 t = path.filefind(f.name, alt_dirs)
257 t = path.filefind(f.name, alt_dirs)
260 # print('found:',t)
258 # print('found:',t)
261
259
262
260
263 @dec.skip_if_not_win32
261 @dec.skip_if_not_win32
264 def test_get_long_path_name_win32():
262 def test_get_long_path_name_win32():
265 with TemporaryDirectory() as tmpdir:
263 with TemporaryDirectory() as tmpdir:
266
264
267 # 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
268 # path component, so ensure we include the long form of it
266 # path component, so ensure we include the long form of it
269 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')
270 os.makedirs(long_path)
268 os.makedirs(long_path)
271
269
272 # Test to see if the short path evaluates correctly.
270 # Test to see if the short path evaluates correctly.
273 short_path = os.path.join(tmpdir, 'THISIS~1')
271 short_path = os.path.join(tmpdir, 'THISIS~1')
274 evaluated_path = path.get_long_path_name(short_path)
272 evaluated_path = path.get_long_path_name(short_path)
275 assert evaluated_path.lower() == long_path.lower()
273 assert evaluated_path.lower() == long_path.lower()
276
274
277
275
278 @dec.skip_win32
276 @dec.skip_win32
279 def test_get_long_path_name():
277 def test_get_long_path_name():
280 p = path.get_long_path_name("/usr/local")
278 p = path.get_long_path_name("/usr/local")
281 assert p == "/usr/local"
279 assert p == "/usr/local"
282
280
283
281
284 class TestRaiseDeprecation(unittest.TestCase):
282 @dec.skip_win32 # can't create not-user-writable dir on win
283 @with_environment
284 def test_not_writable_ipdir():
285 tmpdir = tempfile.mkdtemp()
286 os.name = "posix"
287 env.pop("IPYTHON_DIR", None)
288 env.pop("IPYTHONDIR", None)
289 env.pop("XDG_CONFIG_HOME", None)
290 env["HOME"] = tmpdir
291 ipdir = os.path.join(tmpdir, ".ipython")
292 os.mkdir(ipdir, 0o555)
293 try:
294 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
295 except IOError:
296 pass
297 else:
298 # I can still write to an unwritable dir,
299 # assume I'm root and skip the test
300 pytest.skip("I can't create directories that I can't write to")
301
302 with pytest.warns(UserWarning, match="is not a writable location"):
303 ipdir = paths.get_ipython_dir()
304 env.pop("IPYTHON_DIR", None)
285
305
286 @dec.skip_win32 # can't create not-user-writable dir on win
287 @with_environment
288 def test_not_writable_ipdir(self):
289 tmpdir = tempfile.mkdtemp()
290 os.name = "posix"
291 env.pop('IPYTHON_DIR', None)
292 env.pop('IPYTHONDIR', None)
293 env.pop('XDG_CONFIG_HOME', None)
294 env['HOME'] = tmpdir
295 ipdir = os.path.join(tmpdir, '.ipython')
296 os.mkdir(ipdir, 0o555)
297 try:
298 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
299 except IOError:
300 pass
301 else:
302 # I can still write to an unwritable dir,
303 # assume I'm root and skip the test
304 pytest.skip("I can't create directories that I can't write to")
305
306 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
307 ipdir = paths.get_ipython_dir()
308 env.pop('IPYTHON_DIR', None)
309
306
310 @with_environment
307 @with_environment
311 def test_get_py_filename():
308 def test_get_py_filename():
312 os.chdir(TMP_TEST_DIR)
309 os.chdir(TMP_TEST_DIR)
313 with make_tempfile("foo.py"):
310 with make_tempfile("foo.py"):
314 assert path.get_py_filename("foo.py") == "foo.py"
311 assert path.get_py_filename("foo.py") == "foo.py"
315 assert path.get_py_filename("foo") == "foo.py"
312 assert path.get_py_filename("foo") == "foo.py"
316 with make_tempfile("foo"):
313 with make_tempfile("foo"):
317 assert path.get_py_filename("foo") == "foo"
314 assert path.get_py_filename("foo") == "foo"
318 pytest.raises(IOError, path.get_py_filename, "foo.py")
315 pytest.raises(IOError, path.get_py_filename, "foo.py")
319 pytest.raises(IOError, path.get_py_filename, "foo")
316 pytest.raises(IOError, path.get_py_filename, "foo")
320 pytest.raises(IOError, path.get_py_filename, "foo.py")
317 pytest.raises(IOError, path.get_py_filename, "foo.py")
321 true_fn = "foo with spaces.py"
318 true_fn = "foo with spaces.py"
322 with make_tempfile(true_fn):
319 with make_tempfile(true_fn):
323 assert path.get_py_filename("foo with spaces") == true_fn
320 assert path.get_py_filename("foo with spaces") == true_fn
324 assert path.get_py_filename("foo with spaces.py") == true_fn
321 assert path.get_py_filename("foo with spaces.py") == true_fn
325 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
322 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
326 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
323 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
327
324
328 @onlyif_unicode_paths
325 @onlyif_unicode_paths
329 def test_unicode_in_filename():
326 def test_unicode_in_filename():
330 """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
331 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.
332
329
333 https://github.com/ipython/ipython/issues/875
330 https://github.com/ipython/ipython/issues/875
334 """
331 """
335 try:
332 try:
336 # these calls should not throw unicode encode exceptions
333 # these calls should not throw unicode encode exceptions
337 path.get_py_filename('fooéè.py')
334 path.get_py_filename('fooéè.py')
338 except IOError as ex:
335 except IOError as ex:
339 str(ex)
336 str(ex)
340
337
341
338
342 class TestShellGlob(unittest.TestCase):
339 class TestShellGlob(unittest.TestCase):
343
340
344 @classmethod
341 @classmethod
345 def setUpClass(cls):
342 def setUpClass(cls):
346 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
343 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
347 cls.filenames_end_with_b = ['0b', '1b', '2b']
344 cls.filenames_end_with_b = ['0b', '1b', '2b']
348 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
349 cls.tempdir = TemporaryDirectory()
346 cls.tempdir = TemporaryDirectory()
350 td = cls.tempdir.name
347 td = cls.tempdir.name
351
348
352 with cls.in_tempdir():
349 with cls.in_tempdir():
353 # Create empty files
350 # Create empty files
354 for fname in cls.filenames:
351 for fname in cls.filenames:
355 open(os.path.join(td, fname), "w", encoding="utf-8").close()
352 open(os.path.join(td, fname), "w", encoding="utf-8").close()
356
353
357 @classmethod
354 @classmethod
358 def tearDownClass(cls):
355 def tearDownClass(cls):
359 cls.tempdir.cleanup()
356 cls.tempdir.cleanup()
360
357
361 @classmethod
358 @classmethod
362 @contextmanager
359 @contextmanager
363 def in_tempdir(cls):
360 def in_tempdir(cls):
364 save = os.getcwd()
361 save = os.getcwd()
365 try:
362 try:
366 os.chdir(cls.tempdir.name)
363 os.chdir(cls.tempdir.name)
367 yield
364 yield
368 finally:
365 finally:
369 os.chdir(save)
366 os.chdir(save)
370
367
371 def check_match(self, patterns, matches):
368 def check_match(self, patterns, matches):
372 with self.in_tempdir():
369 with self.in_tempdir():
373 # glob returns unordered list. that's why sorted is required.
370 # glob returns unordered list. that's why sorted is required.
374 assert sorted(path.shellglob(patterns)) == sorted(matches)
371 assert sorted(path.shellglob(patterns)) == sorted(matches)
375
372
376 def common_cases(self):
373 def common_cases(self):
377 return [
374 return [
378 (['*'], self.filenames),
375 (['*'], self.filenames),
379 (['a*'], self.filenames_start_with_a),
376 (['a*'], self.filenames_start_with_a),
380 (['*c'], ['*c']),
377 (['*c'], ['*c']),
381 (['*', 'a*', '*b', '*c'], self.filenames
378 (['*', 'a*', '*b', '*c'], self.filenames
382 + self.filenames_start_with_a
379 + self.filenames_start_with_a
383 + self.filenames_end_with_b
380 + self.filenames_end_with_b
384 + ['*c']),
381 + ['*c']),
385 (['a[012]'], self.filenames_start_with_a),
382 (['a[012]'], self.filenames_start_with_a),
386 ]
383 ]
387
384
388 @skip_win32
385 @skip_win32
389 def test_match_posix(self):
386 def test_match_posix(self):
390 for (patterns, matches) in self.common_cases() + [
387 for (patterns, matches) in self.common_cases() + [
391 ([r'\*'], ['*']),
388 ([r'\*'], ['*']),
392 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
389 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
393 ([r'a\[012]'], ['a[012]']),
390 ([r'a\[012]'], ['a[012]']),
394 ]:
391 ]:
395 self.check_match(patterns, matches)
392 self.check_match(patterns, matches)
396
393
397 @skip_if_not_win32
394 @skip_if_not_win32
398 def test_match_windows(self):
395 def test_match_windows(self):
399 for (patterns, matches) in self.common_cases() + [
396 for (patterns, matches) in self.common_cases() + [
400 # In windows, backslash is interpreted as path
397 # In windows, backslash is interpreted as path
401 # separator. Therefore, you can't escape glob
398 # separator. Therefore, you can't escape glob
402 # using it.
399 # using it.
403 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
400 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
404 ([r'a\[012]'], [r'a\[012]']),
401 ([r'a\[012]'], [r'a\[012]']),
405 ]:
402 ]:
406 self.check_match(patterns, matches)
403 self.check_match(patterns, matches)
407
404
408
405
409 @pytest.mark.parametrize(
406 @pytest.mark.parametrize(
410 "globstr, unescaped_globstr",
407 "globstr, unescaped_globstr",
411 [
408 [
412 (r"\*\[\!\]\?", "*[!]?"),
409 (r"\*\[\!\]\?", "*[!]?"),
413 (r"\\*", r"\*"),
410 (r"\\*", r"\*"),
414 (r"\\\*", r"\*"),
411 (r"\\\*", r"\*"),
415 (r"\\a", r"\a"),
412 (r"\\a", r"\a"),
416 (r"\a", r"\a"),
413 (r"\a", r"\a"),
417 ],
414 ],
418 )
415 )
419 def test_unescape_glob(globstr, unescaped_globstr):
416 def test_unescape_glob(globstr, unescaped_globstr):
420 assert path.unescape_glob(globstr) == unescaped_globstr
417 assert path.unescape_glob(globstr) == unescaped_globstr
421
418
422
419
423 @onlyif_unicode_paths
420 @onlyif_unicode_paths
424 def test_ensure_dir_exists():
421 def test_ensure_dir_exists():
425 with TemporaryDirectory() as td:
422 with TemporaryDirectory() as td:
426 d = os.path.join(td, 'βˆ‚ir')
423 d = os.path.join(td, 'βˆ‚ir')
427 path.ensure_dir_exists(d) # create it
424 path.ensure_dir_exists(d) # create it
428 assert os.path.isdir(d)
425 assert os.path.isdir(d)
429 path.ensure_dir_exists(d) # no-op
426 path.ensure_dir_exists(d) # no-op
430 f = os.path.join(td, "Ζ’ile")
427 f = os.path.join(td, "Ζ’ile")
431 open(f, "w", encoding="utf-8").close() # touch
428 open(f, "w", encoding="utf-8").close() # touch
432 with pytest.raises(IOError):
429 with pytest.raises(IOError):
433 path.ensure_dir_exists(f)
430 path.ensure_dir_exists(f)
434
431
435 class TestLinkOrCopy(unittest.TestCase):
432 class TestLinkOrCopy(unittest.TestCase):
436 def setUp(self):
433 def setUp(self):
437 self.tempdir = TemporaryDirectory()
434 self.tempdir = TemporaryDirectory()
438 self.src = self.dst("src")
435 self.src = self.dst("src")
439 with open(self.src, "w", encoding="utf-8") as f:
436 with open(self.src, "w", encoding="utf-8") as f:
440 f.write("Hello, world!")
437 f.write("Hello, world!")
441
438
442 def tearDown(self):
439 def tearDown(self):
443 self.tempdir.cleanup()
440 self.tempdir.cleanup()
444
441
445 def dst(self, *args):
442 def dst(self, *args):
446 return os.path.join(self.tempdir.name, *args)
443 return os.path.join(self.tempdir.name, *args)
447
444
448 def assert_inode_not_equal(self, a, b):
445 def assert_inode_not_equal(self, a, b):
449 assert (
446 assert (
450 os.stat(a).st_ino != os.stat(b).st_ino
447 os.stat(a).st_ino != os.stat(b).st_ino
451 ), "%r and %r do reference the same indoes" % (a, b)
448 ), "%r and %r do reference the same indoes" % (a, b)
452
449
453 def assert_inode_equal(self, a, b):
450 def assert_inode_equal(self, a, b):
454 assert (
451 assert (
455 os.stat(a).st_ino == os.stat(b).st_ino
452 os.stat(a).st_ino == os.stat(b).st_ino
456 ), "%r and %r do not reference the same indoes" % (a, b)
453 ), "%r and %r do not reference the same indoes" % (a, b)
457
454
458 def assert_content_equal(self, a, b):
455 def assert_content_equal(self, a, b):
459 with open(a, "rb") as a_f:
456 with open(a, "rb") as a_f:
460 with open(b, "rb") as b_f:
457 with open(b, "rb") as b_f:
461 assert a_f.read() == b_f.read()
458 assert a_f.read() == b_f.read()
462
459
463 @skip_win32
460 @skip_win32
464 def test_link_successful(self):
461 def test_link_successful(self):
465 dst = self.dst("target")
462 dst = self.dst("target")
466 path.link_or_copy(self.src, dst)
463 path.link_or_copy(self.src, dst)
467 self.assert_inode_equal(self.src, dst)
464 self.assert_inode_equal(self.src, dst)
468
465
469 @skip_win32
466 @skip_win32
470 def test_link_into_dir(self):
467 def test_link_into_dir(self):
471 dst = self.dst("some_dir")
468 dst = self.dst("some_dir")
472 os.mkdir(dst)
469 os.mkdir(dst)
473 path.link_or_copy(self.src, dst)
470 path.link_or_copy(self.src, dst)
474 expected_dst = self.dst("some_dir", os.path.basename(self.src))
471 expected_dst = self.dst("some_dir", os.path.basename(self.src))
475 self.assert_inode_equal(self.src, expected_dst)
472 self.assert_inode_equal(self.src, expected_dst)
476
473
477 @skip_win32
474 @skip_win32
478 def test_target_exists(self):
475 def test_target_exists(self):
479 dst = self.dst("target")
476 dst = self.dst("target")
480 open(dst, "w", encoding="utf-8").close()
477 open(dst, "w", encoding="utf-8").close()
481 path.link_or_copy(self.src, dst)
478 path.link_or_copy(self.src, dst)
482 self.assert_inode_equal(self.src, dst)
479 self.assert_inode_equal(self.src, dst)
483
480
484 @skip_win32
481 @skip_win32
485 def test_no_link(self):
482 def test_no_link(self):
486 real_link = os.link
483 real_link = os.link
487 try:
484 try:
488 del os.link
485 del os.link
489 dst = self.dst("target")
486 dst = self.dst("target")
490 path.link_or_copy(self.src, dst)
487 path.link_or_copy(self.src, dst)
491 self.assert_content_equal(self.src, dst)
488 self.assert_content_equal(self.src, dst)
492 self.assert_inode_not_equal(self.src, dst)
489 self.assert_inode_not_equal(self.src, dst)
493 finally:
490 finally:
494 os.link = real_link
491 os.link = real_link
495
492
496 @skip_if_not_win32
493 @skip_if_not_win32
497 def test_windows(self):
494 def test_windows(self):
498 dst = self.dst("target")
495 dst = self.dst("target")
499 path.link_or_copy(self.src, dst)
496 path.link_or_copy(self.src, dst)
500 self.assert_content_equal(self.src, dst)
497 self.assert_content_equal(self.src, dst)
501
498
502 def test_link_twice(self):
499 def test_link_twice(self):
503 # Linking the same file twice shouldn't leave duplicates around.
500 # Linking the same file twice shouldn't leave duplicates around.
504 # See https://github.com/ipython/ipython/issues/6450
501 # See https://github.com/ipython/ipython/issues/6450
505 dst = self.dst('target')
502 dst = self.dst('target')
506 path.link_or_copy(self.src, dst)
503 path.link_or_copy(self.src, dst)
507 path.link_or_copy(self.src, dst)
504 path.link_or_copy(self.src, dst)
508 self.assert_inode_equal(self.src, dst)
505 self.assert_inode_equal(self.src, dst)
509 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