##// END OF EJS Templates
Merge pull request #13520 from Carreau/main-tempfile-import...
Matthias Bussonnier -
r27548:6d40fd89 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,70 +1,70 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Tests for IPython.core.application"""
2 """Tests for IPython.core.application"""
3
3
4 import os
4 import os
5 import tempfile
5 import tempfile
6
6
7 from tempfile import TemporaryDirectory
7 from traitlets import Unicode
8 from traitlets import Unicode
8
9
9 from IPython.core.application import BaseIPythonApplication
10 from IPython.core.application import BaseIPythonApplication
10 from IPython.testing import decorators as dec
11 from IPython.testing import decorators as dec
11 from IPython.utils.tempdir import TemporaryDirectory
12
12
13
13
14 @dec.onlyif_unicode_paths
14 @dec.onlyif_unicode_paths
15 def test_unicode_cwd():
15 def test_unicode_cwd():
16 """Check that IPython starts with non-ascii characters in the path."""
16 """Check that IPython starts with non-ascii characters in the path."""
17 wd = tempfile.mkdtemp(suffix=u"€")
17 wd = tempfile.mkdtemp(suffix=u"€")
18
18
19 old_wd = os.getcwd()
19 old_wd = os.getcwd()
20 os.chdir(wd)
20 os.chdir(wd)
21 #raise Exception(repr(os.getcwd()))
21 #raise Exception(repr(os.getcwd()))
22 try:
22 try:
23 app = BaseIPythonApplication()
23 app = BaseIPythonApplication()
24 # The lines below are copied from Application.initialize()
24 # The lines below are copied from Application.initialize()
25 app.init_profile_dir()
25 app.init_profile_dir()
26 app.init_config_files()
26 app.init_config_files()
27 app.load_config_file(suppress_errors=False)
27 app.load_config_file(suppress_errors=False)
28 finally:
28 finally:
29 os.chdir(old_wd)
29 os.chdir(old_wd)
30
30
31 @dec.onlyif_unicode_paths
31 @dec.onlyif_unicode_paths
32 def test_unicode_ipdir():
32 def test_unicode_ipdir():
33 """Check that IPython starts with non-ascii characters in the IP dir."""
33 """Check that IPython starts with non-ascii characters in the IP dir."""
34 ipdir = tempfile.mkdtemp(suffix=u"€")
34 ipdir = tempfile.mkdtemp(suffix=u"€")
35
35
36 # Create the config file, so it tries to load it.
36 # Create the config file, so it tries to load it.
37 with open(os.path.join(ipdir, "ipython_config.py"), "w", encoding="utf-8") as f:
37 with open(os.path.join(ipdir, "ipython_config.py"), "w", encoding="utf-8") as f:
38 pass
38 pass
39
39
40 old_ipdir1 = os.environ.pop("IPYTHONDIR", None)
40 old_ipdir1 = os.environ.pop("IPYTHONDIR", None)
41 old_ipdir2 = os.environ.pop("IPYTHON_DIR", None)
41 old_ipdir2 = os.environ.pop("IPYTHON_DIR", None)
42 os.environ["IPYTHONDIR"] = ipdir
42 os.environ["IPYTHONDIR"] = ipdir
43 try:
43 try:
44 app = BaseIPythonApplication()
44 app = BaseIPythonApplication()
45 # The lines below are copied from Application.initialize()
45 # The lines below are copied from Application.initialize()
46 app.init_profile_dir()
46 app.init_profile_dir()
47 app.init_config_files()
47 app.init_config_files()
48 app.load_config_file(suppress_errors=False)
48 app.load_config_file(suppress_errors=False)
49 finally:
49 finally:
50 if old_ipdir1:
50 if old_ipdir1:
51 os.environ["IPYTHONDIR"] = old_ipdir1
51 os.environ["IPYTHONDIR"] = old_ipdir1
52 if old_ipdir2:
52 if old_ipdir2:
53 os.environ["IPYTHONDIR"] = old_ipdir2
53 os.environ["IPYTHONDIR"] = old_ipdir2
54
54
55 def test_cli_priority():
55 def test_cli_priority():
56 with TemporaryDirectory() as td:
56 with TemporaryDirectory() as td:
57
57
58 class TestApp(BaseIPythonApplication):
58 class TestApp(BaseIPythonApplication):
59 test = Unicode().tag(config=True)
59 test = Unicode().tag(config=True)
60
60
61 # Create the config file, so it tries to load it.
61 # Create the config file, so it tries to load it.
62 with open(os.path.join(td, "ipython_config.py"), "w", encoding="utf-8") as f:
62 with open(os.path.join(td, "ipython_config.py"), "w", encoding="utf-8") as f:
63 f.write("c.TestApp.test = 'config file'")
63 f.write("c.TestApp.test = 'config file'")
64
64
65 app = TestApp()
65 app = TestApp()
66 app.initialize(["--profile-dir", td])
66 app.initialize(["--profile-dir", td])
67 assert app.test == "config file"
67 assert app.test == "config file"
68 app = TestApp()
68 app = TestApp()
69 app.initialize(["--profile-dir", td, "--TestApp.test=cli"])
69 app.initialize(["--profile-dir", td, "--TestApp.test=cli"])
70 assert app.test == "cli"
70 assert app.test == "cli"
@@ -1,192 +1,193 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for completerlib.
2 """Tests for completerlib.
3
3
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Imports
7 # Imports
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 import os
10 import os
11 import shutil
11 import shutil
12 import sys
12 import sys
13 import tempfile
13 import tempfile
14 import unittest
14 import unittest
15 from os.path import join
15 from os.path import join
16
16
17 from tempfile import TemporaryDirectory
18
17 from IPython.core.completerlib import magic_run_completer, module_completion, try_import
19 from IPython.core.completerlib import magic_run_completer, module_completion, try_import
18 from IPython.utils.tempdir import TemporaryDirectory
19 from IPython.testing.decorators import onlyif_unicode_paths
20 from IPython.testing.decorators import onlyif_unicode_paths
20
21
21
22
22 class MockEvent(object):
23 class MockEvent(object):
23 def __init__(self, line):
24 def __init__(self, line):
24 self.line = line
25 self.line = line
25
26
26 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
27 # Test functions begin
28 # Test functions begin
28 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
29 class Test_magic_run_completer(unittest.TestCase):
30 class Test_magic_run_completer(unittest.TestCase):
30 files = [u"aao.py", u"a.py", u"b.py", u"aao.txt"]
31 files = [u"aao.py", u"a.py", u"b.py", u"aao.txt"]
31 dirs = [u"adir/", "bdir/"]
32 dirs = [u"adir/", "bdir/"]
32
33
33 def setUp(self):
34 def setUp(self):
34 self.BASETESTDIR = tempfile.mkdtemp()
35 self.BASETESTDIR = tempfile.mkdtemp()
35 for fil in self.files:
36 for fil in self.files:
36 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
37 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
37 sfile.write("pass\n")
38 sfile.write("pass\n")
38 for d in self.dirs:
39 for d in self.dirs:
39 os.mkdir(join(self.BASETESTDIR, d))
40 os.mkdir(join(self.BASETESTDIR, d))
40
41
41 self.oldpath = os.getcwd()
42 self.oldpath = os.getcwd()
42 os.chdir(self.BASETESTDIR)
43 os.chdir(self.BASETESTDIR)
43
44
44 def tearDown(self):
45 def tearDown(self):
45 os.chdir(self.oldpath)
46 os.chdir(self.oldpath)
46 shutil.rmtree(self.BASETESTDIR)
47 shutil.rmtree(self.BASETESTDIR)
47
48
48 def test_1(self):
49 def test_1(self):
49 """Test magic_run_completer, should match two alternatives
50 """Test magic_run_completer, should match two alternatives
50 """
51 """
51 event = MockEvent(u"%run a")
52 event = MockEvent(u"%run a")
52 mockself = None
53 mockself = None
53 match = set(magic_run_completer(mockself, event))
54 match = set(magic_run_completer(mockself, event))
54 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
55 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
55
56
56 def test_2(self):
57 def test_2(self):
57 """Test magic_run_completer, should match one alternative
58 """Test magic_run_completer, should match one alternative
58 """
59 """
59 event = MockEvent(u"%run aa")
60 event = MockEvent(u"%run aa")
60 mockself = None
61 mockself = None
61 match = set(magic_run_completer(mockself, event))
62 match = set(magic_run_completer(mockself, event))
62 self.assertEqual(match, {u"aao.py"})
63 self.assertEqual(match, {u"aao.py"})
63
64
64 def test_3(self):
65 def test_3(self):
65 """Test magic_run_completer with unterminated " """
66 """Test magic_run_completer with unterminated " """
66 event = MockEvent(u'%run "a')
67 event = MockEvent(u'%run "a')
67 mockself = None
68 mockself = None
68 match = set(magic_run_completer(mockself, event))
69 match = set(magic_run_completer(mockself, event))
69 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
70 self.assertEqual(match, {u"a.py", u"aao.py", u"adir/"})
70
71
71 def test_completion_more_args(self):
72 def test_completion_more_args(self):
72 event = MockEvent(u'%run a.py ')
73 event = MockEvent(u'%run a.py ')
73 match = set(magic_run_completer(None, event))
74 match = set(magic_run_completer(None, event))
74 self.assertEqual(match, set(self.files + self.dirs))
75 self.assertEqual(match, set(self.files + self.dirs))
75
76
76 def test_completion_in_dir(self):
77 def test_completion_in_dir(self):
77 # Github issue #3459
78 # Github issue #3459
78 event = MockEvent(u'%run a.py {}'.format(join(self.BASETESTDIR, 'a')))
79 event = MockEvent(u'%run a.py {}'.format(join(self.BASETESTDIR, 'a')))
79 print(repr(event.line))
80 print(repr(event.line))
80 match = set(magic_run_completer(None, event))
81 match = set(magic_run_completer(None, event))
81 # We specifically use replace here rather than normpath, because
82 # We specifically use replace here rather than normpath, because
82 # at one point there were duplicates 'adir' and 'adir/', and normpath
83 # at one point there were duplicates 'adir' and 'adir/', and normpath
83 # would hide the failure for that.
84 # would hide the failure for that.
84 self.assertEqual(match, {join(self.BASETESTDIR, f).replace('\\','/')
85 self.assertEqual(match, {join(self.BASETESTDIR, f).replace('\\','/')
85 for f in (u'a.py', u'aao.py', u'aao.txt', u'adir/')})
86 for f in (u'a.py', u'aao.py', u'aao.txt', u'adir/')})
86
87
87 class Test_magic_run_completer_nonascii(unittest.TestCase):
88 class Test_magic_run_completer_nonascii(unittest.TestCase):
88 @onlyif_unicode_paths
89 @onlyif_unicode_paths
89 def setUp(self):
90 def setUp(self):
90 self.BASETESTDIR = tempfile.mkdtemp()
91 self.BASETESTDIR = tempfile.mkdtemp()
91 for fil in [u"aaΓΈ.py", u"a.py", u"b.py"]:
92 for fil in [u"aaΓΈ.py", u"a.py", u"b.py"]:
92 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
93 with open(join(self.BASETESTDIR, fil), "w", encoding="utf-8") as sfile:
93 sfile.write("pass\n")
94 sfile.write("pass\n")
94 self.oldpath = os.getcwd()
95 self.oldpath = os.getcwd()
95 os.chdir(self.BASETESTDIR)
96 os.chdir(self.BASETESTDIR)
96
97
97 def tearDown(self):
98 def tearDown(self):
98 os.chdir(self.oldpath)
99 os.chdir(self.oldpath)
99 shutil.rmtree(self.BASETESTDIR)
100 shutil.rmtree(self.BASETESTDIR)
100
101
101 @onlyif_unicode_paths
102 @onlyif_unicode_paths
102 def test_1(self):
103 def test_1(self):
103 """Test magic_run_completer, should match two alternatives
104 """Test magic_run_completer, should match two alternatives
104 """
105 """
105 event = MockEvent(u"%run a")
106 event = MockEvent(u"%run a")
106 mockself = None
107 mockself = None
107 match = set(magic_run_completer(mockself, event))
108 match = set(magic_run_completer(mockself, event))
108 self.assertEqual(match, {u"a.py", u"aaΓΈ.py"})
109 self.assertEqual(match, {u"a.py", u"aaΓΈ.py"})
109
110
110 @onlyif_unicode_paths
111 @onlyif_unicode_paths
111 def test_2(self):
112 def test_2(self):
112 """Test magic_run_completer, should match one alternative
113 """Test magic_run_completer, should match one alternative
113 """
114 """
114 event = MockEvent(u"%run aa")
115 event = MockEvent(u"%run aa")
115 mockself = None
116 mockself = None
116 match = set(magic_run_completer(mockself, event))
117 match = set(magic_run_completer(mockself, event))
117 self.assertEqual(match, {u"aaΓΈ.py"})
118 self.assertEqual(match, {u"aaΓΈ.py"})
118
119
119 @onlyif_unicode_paths
120 @onlyif_unicode_paths
120 def test_3(self):
121 def test_3(self):
121 """Test magic_run_completer with unterminated " """
122 """Test magic_run_completer with unterminated " """
122 event = MockEvent(u'%run "a')
123 event = MockEvent(u'%run "a')
123 mockself = None
124 mockself = None
124 match = set(magic_run_completer(mockself, event))
125 match = set(magic_run_completer(mockself, event))
125 self.assertEqual(match, {u"a.py", u"aaΓΈ.py"})
126 self.assertEqual(match, {u"a.py", u"aaΓΈ.py"})
126
127
127 # module_completer:
128 # module_completer:
128
129
129 def test_import_invalid_module():
130 def test_import_invalid_module():
130 """Testing of issue https://github.com/ipython/ipython/issues/1107"""
131 """Testing of issue https://github.com/ipython/ipython/issues/1107"""
131 invalid_module_names = {'foo-bar', 'foo:bar', '10foo'}
132 invalid_module_names = {'foo-bar', 'foo:bar', '10foo'}
132 valid_module_names = {'foobar'}
133 valid_module_names = {'foobar'}
133 with TemporaryDirectory() as tmpdir:
134 with TemporaryDirectory() as tmpdir:
134 sys.path.insert( 0, tmpdir )
135 sys.path.insert( 0, tmpdir )
135 for name in invalid_module_names | valid_module_names:
136 for name in invalid_module_names | valid_module_names:
136 filename = os.path.join(tmpdir, name + ".py")
137 filename = os.path.join(tmpdir, name + ".py")
137 open(filename, "w", encoding="utf-8").close()
138 open(filename, "w", encoding="utf-8").close()
138
139
139 s = set( module_completion('import foo') )
140 s = set( module_completion('import foo') )
140 intersection = s.intersection(invalid_module_names)
141 intersection = s.intersection(invalid_module_names)
141 assert intersection == set()
142 assert intersection == set()
142
143
143 assert valid_module_names.issubset(s), valid_module_names.intersection(s)
144 assert valid_module_names.issubset(s), valid_module_names.intersection(s)
144
145
145
146
146 def test_bad_module_all():
147 def test_bad_module_all():
147 """Test module with invalid __all__
148 """Test module with invalid __all__
148
149
149 https://github.com/ipython/ipython/issues/9678
150 https://github.com/ipython/ipython/issues/9678
150 """
151 """
151 testsdir = os.path.dirname(__file__)
152 testsdir = os.path.dirname(__file__)
152 sys.path.insert(0, testsdir)
153 sys.path.insert(0, testsdir)
153 try:
154 try:
154 results = module_completion("from bad_all import ")
155 results = module_completion("from bad_all import ")
155 assert "puppies" in results
156 assert "puppies" in results
156 for r in results:
157 for r in results:
157 assert isinstance(r, str)
158 assert isinstance(r, str)
158
159
159 # bad_all doesn't contain submodules, but this completion
160 # bad_all doesn't contain submodules, but this completion
160 # should finish without raising an exception:
161 # should finish without raising an exception:
161 results = module_completion("import bad_all.")
162 results = module_completion("import bad_all.")
162 assert results == []
163 assert results == []
163 finally:
164 finally:
164 sys.path.remove(testsdir)
165 sys.path.remove(testsdir)
165
166
166
167
167 def test_module_without_init():
168 def test_module_without_init():
168 """
169 """
169 Test module without __init__.py.
170 Test module without __init__.py.
170
171
171 https://github.com/ipython/ipython/issues/11226
172 https://github.com/ipython/ipython/issues/11226
172 """
173 """
173 fake_module_name = "foo"
174 fake_module_name = "foo"
174 with TemporaryDirectory() as tmpdir:
175 with TemporaryDirectory() as tmpdir:
175 sys.path.insert(0, tmpdir)
176 sys.path.insert(0, tmpdir)
176 try:
177 try:
177 os.makedirs(os.path.join(tmpdir, fake_module_name))
178 os.makedirs(os.path.join(tmpdir, fake_module_name))
178 s = try_import(mod=fake_module_name)
179 s = try_import(mod=fake_module_name)
179 assert s == []
180 assert s == []
180 finally:
181 finally:
181 sys.path.remove(tmpdir)
182 sys.path.remove(tmpdir)
182
183
183
184
184 def test_valid_exported_submodules():
185 def test_valid_exported_submodules():
185 """
186 """
186 Test checking exported (__all__) objects are submodules
187 Test checking exported (__all__) objects are submodules
187 """
188 """
188 results = module_completion("import os.pa")
189 results = module_completion("import os.pa")
189 # ensure we get a valid submodule:
190 # ensure we get a valid submodule:
190 assert "os.path" in results
191 assert "os.path" in results
191 # ensure we don't get objects that aren't submodules:
192 # ensure we don't get objects that aren't submodules:
192 assert "os.pathconf" not in results
193 assert "os.pathconf" not in results
@@ -1,94 +1,95 b''
1 import os.path
1 import os.path
2
2
3 from tempfile import TemporaryDirectory
4
3 import IPython.testing.tools as tt
5 import IPython.testing.tools as tt
4 from IPython.utils.syspathcontext import prepended_to_syspath
6 from IPython.utils.syspathcontext import prepended_to_syspath
5 from IPython.utils.tempdir import TemporaryDirectory
6
7
7 ext1_content = """
8 ext1_content = """
8 def load_ipython_extension(ip):
9 def load_ipython_extension(ip):
9 print("Running ext1 load")
10 print("Running ext1 load")
10
11
11 def unload_ipython_extension(ip):
12 def unload_ipython_extension(ip):
12 print("Running ext1 unload")
13 print("Running ext1 unload")
13 """
14 """
14
15
15 ext2_content = """
16 ext2_content = """
16 def load_ipython_extension(ip):
17 def load_ipython_extension(ip):
17 print("Running ext2 load")
18 print("Running ext2 load")
18 """
19 """
19
20
20 ext3_content = """
21 ext3_content = """
21 def load_ipython_extension(ip):
22 def load_ipython_extension(ip):
22 ip2 = get_ipython()
23 ip2 = get_ipython()
23 print(ip is ip2)
24 print(ip is ip2)
24 """
25 """
25
26
26 def test_extension_loading():
27 def test_extension_loading():
27 em = get_ipython().extension_manager
28 em = get_ipython().extension_manager
28 with TemporaryDirectory() as td:
29 with TemporaryDirectory() as td:
29 ext1 = os.path.join(td, "ext1.py")
30 ext1 = os.path.join(td, "ext1.py")
30 with open(ext1, "w", encoding="utf-8") as f:
31 with open(ext1, "w", encoding="utf-8") as f:
31 f.write(ext1_content)
32 f.write(ext1_content)
32
33
33 ext2 = os.path.join(td, "ext2.py")
34 ext2 = os.path.join(td, "ext2.py")
34 with open(ext2, "w", encoding="utf-8") as f:
35 with open(ext2, "w", encoding="utf-8") as f:
35 f.write(ext2_content)
36 f.write(ext2_content)
36
37
37 with prepended_to_syspath(td):
38 with prepended_to_syspath(td):
38 assert 'ext1' not in em.loaded
39 assert 'ext1' not in em.loaded
39 assert 'ext2' not in em.loaded
40 assert 'ext2' not in em.loaded
40
41
41 # Load extension
42 # Load extension
42 with tt.AssertPrints("Running ext1 load"):
43 with tt.AssertPrints("Running ext1 load"):
43 assert em.load_extension('ext1') is None
44 assert em.load_extension('ext1') is None
44 assert 'ext1' in em.loaded
45 assert 'ext1' in em.loaded
45
46
46 # Should refuse to load it again
47 # Should refuse to load it again
47 with tt.AssertNotPrints("Running ext1 load"):
48 with tt.AssertNotPrints("Running ext1 load"):
48 assert em.load_extension('ext1') == 'already loaded'
49 assert em.load_extension('ext1') == 'already loaded'
49
50
50 # Reload
51 # Reload
51 with tt.AssertPrints("Running ext1 unload"):
52 with tt.AssertPrints("Running ext1 unload"):
52 with tt.AssertPrints("Running ext1 load", suppress=False):
53 with tt.AssertPrints("Running ext1 load", suppress=False):
53 em.reload_extension('ext1')
54 em.reload_extension('ext1')
54
55
55 # Unload
56 # Unload
56 with tt.AssertPrints("Running ext1 unload"):
57 with tt.AssertPrints("Running ext1 unload"):
57 assert em.unload_extension('ext1') is None
58 assert em.unload_extension('ext1') is None
58
59
59 # Can't unload again
60 # Can't unload again
60 with tt.AssertNotPrints("Running ext1 unload"):
61 with tt.AssertNotPrints("Running ext1 unload"):
61 assert em.unload_extension('ext1') == 'not loaded'
62 assert em.unload_extension('ext1') == 'not loaded'
62 assert em.unload_extension('ext2') == 'not loaded'
63 assert em.unload_extension('ext2') == 'not loaded'
63
64
64 # Load extension 2
65 # Load extension 2
65 with tt.AssertPrints("Running ext2 load"):
66 with tt.AssertPrints("Running ext2 load"):
66 assert em.load_extension('ext2') is None
67 assert em.load_extension('ext2') is None
67
68
68 # Can't unload this
69 # Can't unload this
69 assert em.unload_extension('ext2') == 'no unload function'
70 assert em.unload_extension('ext2') == 'no unload function'
70
71
71 # But can reload it
72 # But can reload it
72 with tt.AssertPrints("Running ext2 load"):
73 with tt.AssertPrints("Running ext2 load"):
73 em.reload_extension('ext2')
74 em.reload_extension('ext2')
74
75
75
76
76 def test_extension_builtins():
77 def test_extension_builtins():
77 em = get_ipython().extension_manager
78 em = get_ipython().extension_manager
78 with TemporaryDirectory() as td:
79 with TemporaryDirectory() as td:
79 ext3 = os.path.join(td, "ext3.py")
80 ext3 = os.path.join(td, "ext3.py")
80 with open(ext3, "w", encoding="utf-8") as f:
81 with open(ext3, "w", encoding="utf-8") as f:
81 f.write(ext3_content)
82 f.write(ext3_content)
82
83
83 assert 'ext3' not in em.loaded
84 assert 'ext3' not in em.loaded
84
85
85 with prepended_to_syspath(td):
86 with prepended_to_syspath(td):
86 # Load extension
87 # Load extension
87 with tt.AssertPrints("True"):
88 with tt.AssertPrints("True"):
88 assert em.load_extension('ext3') is None
89 assert em.load_extension('ext3') is None
89 assert 'ext3' in em.loaded
90 assert 'ext3' in em.loaded
90
91
91
92
92 def test_non_extension():
93 def test_non_extension():
93 em = get_ipython().extension_manager
94 em = get_ipython().extension_manager
94 assert em.load_extension("sys") == "no load function"
95 assert em.load_extension("sys") == "no load function"
@@ -1,227 +1,229 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Tests for the IPython tab-completion machinery.
2 """Tests for the IPython tab-completion machinery.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Module imports
5 # Module imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # stdlib
8 # stdlib
9 import io
9 import io
10 from pathlib import Path
10 import sqlite3
11 import sys
11 import sys
12 import tempfile
12 import tempfile
13 from datetime import datetime
13 from datetime import datetime
14 import sqlite3
14 from pathlib import Path
15
15
16 from tempfile import TemporaryDirectory
16 # our own packages
17 # our own packages
17 from traitlets.config.loader import Config
18 from traitlets.config.loader import Config
18 from IPython.utils.tempdir import TemporaryDirectory
19
19 from IPython.core.history import HistoryManager, extract_hist_ranges
20 from IPython.core.history import HistoryManager, extract_hist_ranges
20
21
22
21 def test_proper_default_encoding():
23 def test_proper_default_encoding():
22 assert sys.getdefaultencoding() == "utf-8"
24 assert sys.getdefaultencoding() == "utf-8"
23
25
24 def test_history():
26 def test_history():
25 ip = get_ipython()
27 ip = get_ipython()
26 with TemporaryDirectory() as tmpdir:
28 with TemporaryDirectory() as tmpdir:
27 tmp_path = Path(tmpdir)
29 tmp_path = Path(tmpdir)
28 hist_manager_ori = ip.history_manager
30 hist_manager_ori = ip.history_manager
29 hist_file = tmp_path / "history.sqlite"
31 hist_file = tmp_path / "history.sqlite"
30 try:
32 try:
31 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
33 ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file)
32 hist = ["a=1", "def f():\n test = 1\n return test", "b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
34 hist = ["a=1", "def f():\n test = 1\n return test", "b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
33 for i, h in enumerate(hist, start=1):
35 for i, h in enumerate(hist, start=1):
34 ip.history_manager.store_inputs(i, h)
36 ip.history_manager.store_inputs(i, h)
35
37
36 ip.history_manager.db_log_output = True
38 ip.history_manager.db_log_output = True
37 # Doesn't match the input, but we'll just check it's stored.
39 # Doesn't match the input, but we'll just check it's stored.
38 ip.history_manager.output_hist_reprs[3] = "spam"
40 ip.history_manager.output_hist_reprs[3] = "spam"
39 ip.history_manager.store_output(3)
41 ip.history_manager.store_output(3)
40
42
41 assert ip.history_manager.input_hist_raw == [""] + hist
43 assert ip.history_manager.input_hist_raw == [""] + hist
42
44
43 # Detailed tests for _get_range_session
45 # Detailed tests for _get_range_session
44 grs = ip.history_manager._get_range_session
46 grs = ip.history_manager._get_range_session
45 assert list(grs(start=2, stop=-1)) == list(zip([0], [2], hist[1:-1]))
47 assert list(grs(start=2, stop=-1)) == list(zip([0], [2], hist[1:-1]))
46 assert list(grs(start=-2)) == list(zip([0, 0], [2, 3], hist[-2:]))
48 assert list(grs(start=-2)) == list(zip([0, 0], [2, 3], hist[-2:]))
47 assert list(grs(output=True)) == list(
49 assert list(grs(output=True)) == list(
48 zip([0, 0, 0], [1, 2, 3], zip(hist, [None, None, "spam"]))
50 zip([0, 0, 0], [1, 2, 3], zip(hist, [None, None, "spam"]))
49 )
51 )
50
52
51 # Check whether specifying a range beyond the end of the current
53 # Check whether specifying a range beyond the end of the current
52 # session results in an error (gh-804)
54 # session results in an error (gh-804)
53 ip.magic('%hist 2-500')
55 ip.magic('%hist 2-500')
54
56
55 # Check that we can write non-ascii characters to a file
57 # Check that we can write non-ascii characters to a file
56 ip.magic("%%hist -f %s" % (tmp_path / "test1"))
58 ip.magic("%%hist -f %s" % (tmp_path / "test1"))
57 ip.magic("%%hist -pf %s" % (tmp_path / "test2"))
59 ip.magic("%%hist -pf %s" % (tmp_path / "test2"))
58 ip.magic("%%hist -nf %s" % (tmp_path / "test3"))
60 ip.magic("%%hist -nf %s" % (tmp_path / "test3"))
59 ip.magic("%%save %s 1-10" % (tmp_path / "test4"))
61 ip.magic("%%save %s 1-10" % (tmp_path / "test4"))
60
62
61 # New session
63 # New session
62 ip.history_manager.reset()
64 ip.history_manager.reset()
63 newcmds = ["z=5", "class X(object):\n pass", "k='p'", "z=5"]
65 newcmds = ["z=5", "class X(object):\n pass", "k='p'", "z=5"]
64 for i, cmd in enumerate(newcmds, start=1):
66 for i, cmd in enumerate(newcmds, start=1):
65 ip.history_manager.store_inputs(i, cmd)
67 ip.history_manager.store_inputs(i, cmd)
66 gothist = ip.history_manager.get_range(start=1, stop=4)
68 gothist = ip.history_manager.get_range(start=1, stop=4)
67 assert list(gothist) == list(zip([0, 0, 0], [1, 2, 3], newcmds))
69 assert list(gothist) == list(zip([0, 0, 0], [1, 2, 3], newcmds))
68 # Previous session:
70 # Previous session:
69 gothist = ip.history_manager.get_range(-1, 1, 4)
71 gothist = ip.history_manager.get_range(-1, 1, 4)
70 assert list(gothist) == list(zip([1, 1, 1], [1, 2, 3], hist))
72 assert list(gothist) == list(zip([1, 1, 1], [1, 2, 3], hist))
71
73
72 newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)]
74 newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)]
73
75
74 # Check get_hist_tail
76 # Check get_hist_tail
75 gothist = ip.history_manager.get_tail(5, output=True,
77 gothist = ip.history_manager.get_tail(5, output=True,
76 include_latest=True)
78 include_latest=True)
77 expected = [(1, 3, (hist[-1], "spam"))] \
79 expected = [(1, 3, (hist[-1], "spam"))] \
78 + [(s, n, (c, None)) for (s, n, c) in newhist]
80 + [(s, n, (c, None)) for (s, n, c) in newhist]
79 assert list(gothist) == expected
81 assert list(gothist) == expected
80
82
81 gothist = ip.history_manager.get_tail(2)
83 gothist = ip.history_manager.get_tail(2)
82 expected = newhist[-3:-1]
84 expected = newhist[-3:-1]
83 assert list(gothist) == expected
85 assert list(gothist) == expected
84
86
85 # Check get_hist_search
87 # Check get_hist_search
86
88
87 gothist = ip.history_manager.search("*test*")
89 gothist = ip.history_manager.search("*test*")
88 assert list(gothist) == [(1, 2, hist[1])]
90 assert list(gothist) == [(1, 2, hist[1])]
89
91
90 gothist = ip.history_manager.search("*=*")
92 gothist = ip.history_manager.search("*=*")
91 assert list(gothist) == [
93 assert list(gothist) == [
92 (1, 1, hist[0]),
94 (1, 1, hist[0]),
93 (1, 2, hist[1]),
95 (1, 2, hist[1]),
94 (1, 3, hist[2]),
96 (1, 3, hist[2]),
95 newhist[0],
97 newhist[0],
96 newhist[2],
98 newhist[2],
97 newhist[3],
99 newhist[3],
98 ]
100 ]
99
101
100 gothist = ip.history_manager.search("*=*", n=4)
102 gothist = ip.history_manager.search("*=*", n=4)
101 assert list(gothist) == [
103 assert list(gothist) == [
102 (1, 3, hist[2]),
104 (1, 3, hist[2]),
103 newhist[0],
105 newhist[0],
104 newhist[2],
106 newhist[2],
105 newhist[3],
107 newhist[3],
106 ]
108 ]
107
109
108 gothist = ip.history_manager.search("*=*", unique=True)
110 gothist = ip.history_manager.search("*=*", unique=True)
109 assert list(gothist) == [
111 assert list(gothist) == [
110 (1, 1, hist[0]),
112 (1, 1, hist[0]),
111 (1, 2, hist[1]),
113 (1, 2, hist[1]),
112 (1, 3, hist[2]),
114 (1, 3, hist[2]),
113 newhist[2],
115 newhist[2],
114 newhist[3],
116 newhist[3],
115 ]
117 ]
116
118
117 gothist = ip.history_manager.search("*=*", unique=True, n=3)
119 gothist = ip.history_manager.search("*=*", unique=True, n=3)
118 assert list(gothist) == [(1, 3, hist[2]), newhist[2], newhist[3]]
120 assert list(gothist) == [(1, 3, hist[2]), newhist[2], newhist[3]]
119
121
120 gothist = ip.history_manager.search("b*", output=True)
122 gothist = ip.history_manager.search("b*", output=True)
121 assert list(gothist) == [(1, 3, (hist[2], "spam"))]
123 assert list(gothist) == [(1, 3, (hist[2], "spam"))]
122
124
123 # Cross testing: check that magic %save can get previous session.
125 # Cross testing: check that magic %save can get previous session.
124 testfilename = (tmp_path / "test.py").resolve()
126 testfilename = (tmp_path / "test.py").resolve()
125 ip.magic("save " + str(testfilename) + " ~1/1-3")
127 ip.magic("save " + str(testfilename) + " ~1/1-3")
126 with io.open(testfilename, encoding="utf-8") as testfile:
128 with io.open(testfilename, encoding="utf-8") as testfile:
127 assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n"
129 assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n"
128
130
129 # Duplicate line numbers - check that it doesn't crash, and
131 # Duplicate line numbers - check that it doesn't crash, and
130 # gets a new session
132 # gets a new session
131 ip.history_manager.store_inputs(1, "rogue")
133 ip.history_manager.store_inputs(1, "rogue")
132 ip.history_manager.writeout_cache()
134 ip.history_manager.writeout_cache()
133 assert ip.history_manager.session_number == 3
135 assert ip.history_manager.session_number == 3
134
136
135 # Check that session and line values are not just max values
137 # Check that session and line values are not just max values
136 sessid, lineno, entry = newhist[-1]
138 sessid, lineno, entry = newhist[-1]
137 assert lineno > 1
139 assert lineno > 1
138 ip.history_manager.reset()
140 ip.history_manager.reset()
139 lineno = 1
141 lineno = 1
140 ip.history_manager.store_inputs(lineno, entry)
142 ip.history_manager.store_inputs(lineno, entry)
141 gothist = ip.history_manager.search("*=*", unique=True)
143 gothist = ip.history_manager.search("*=*", unique=True)
142 hist = list(gothist)[-1]
144 hist = list(gothist)[-1]
143 assert sessid < hist[0]
145 assert sessid < hist[0]
144 assert hist[1:] == (lineno, entry)
146 assert hist[1:] == (lineno, entry)
145 finally:
147 finally:
146 # Ensure saving thread is shut down before we try to clean up the files
148 # Ensure saving thread is shut down before we try to clean up the files
147 ip.history_manager.save_thread.stop()
149 ip.history_manager.save_thread.stop()
148 # Forcibly close database rather than relying on garbage collection
150 # Forcibly close database rather than relying on garbage collection
149 ip.history_manager.db.close()
151 ip.history_manager.db.close()
150 # Restore history manager
152 # Restore history manager
151 ip.history_manager = hist_manager_ori
153 ip.history_manager = hist_manager_ori
152
154
153
155
154 def test_extract_hist_ranges():
156 def test_extract_hist_ranges():
155 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/"
157 instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/"
156 expected = [(0, 1, 2), # 0 == current session
158 expected = [(0, 1, 2), # 0 == current session
157 (2, 3, 4),
159 (2, 3, 4),
158 (-4, 5, 7),
160 (-4, 5, 7),
159 (-4, 7, 10),
161 (-4, 7, 10),
160 (-9, 2, None), # None == to end
162 (-9, 2, None), # None == to end
161 (-8, 1, None),
163 (-8, 1, None),
162 (-7, 1, 6),
164 (-7, 1, 6),
163 (-10, 1, None)]
165 (-10, 1, None)]
164 actual = list(extract_hist_ranges(instr))
166 actual = list(extract_hist_ranges(instr))
165 assert actual == expected
167 assert actual == expected
166
168
167
169
168 def test_extract_hist_ranges_empty_str():
170 def test_extract_hist_ranges_empty_str():
169 instr = ""
171 instr = ""
170 expected = [(0, 1, None)] # 0 == current session, None == to end
172 expected = [(0, 1, None)] # 0 == current session, None == to end
171 actual = list(extract_hist_ranges(instr))
173 actual = list(extract_hist_ranges(instr))
172 assert actual == expected
174 assert actual == expected
173
175
174
176
175 def test_magic_rerun():
177 def test_magic_rerun():
176 """Simple test for %rerun (no args -> rerun last line)"""
178 """Simple test for %rerun (no args -> rerun last line)"""
177 ip = get_ipython()
179 ip = get_ipython()
178 ip.run_cell("a = 10", store_history=True)
180 ip.run_cell("a = 10", store_history=True)
179 ip.run_cell("a += 1", store_history=True)
181 ip.run_cell("a += 1", store_history=True)
180 assert ip.user_ns["a"] == 11
182 assert ip.user_ns["a"] == 11
181 ip.run_cell("%rerun", store_history=True)
183 ip.run_cell("%rerun", store_history=True)
182 assert ip.user_ns["a"] == 12
184 assert ip.user_ns["a"] == 12
183
185
184 def test_timestamp_type():
186 def test_timestamp_type():
185 ip = get_ipython()
187 ip = get_ipython()
186 info = ip.history_manager.get_session_info()
188 info = ip.history_manager.get_session_info()
187 assert isinstance(info[1], datetime)
189 assert isinstance(info[1], datetime)
188
190
189 def test_hist_file_config():
191 def test_hist_file_config():
190 cfg = Config()
192 cfg = Config()
191 tfile = tempfile.NamedTemporaryFile(delete=False)
193 tfile = tempfile.NamedTemporaryFile(delete=False)
192 cfg.HistoryManager.hist_file = Path(tfile.name)
194 cfg.HistoryManager.hist_file = Path(tfile.name)
193 try:
195 try:
194 hm = HistoryManager(shell=get_ipython(), config=cfg)
196 hm = HistoryManager(shell=get_ipython(), config=cfg)
195 assert hm.hist_file == cfg.HistoryManager.hist_file
197 assert hm.hist_file == cfg.HistoryManager.hist_file
196 finally:
198 finally:
197 try:
199 try:
198 Path(tfile.name).unlink()
200 Path(tfile.name).unlink()
199 except OSError:
201 except OSError:
200 # same catch as in testing.tools.TempFileMixin
202 # same catch as in testing.tools.TempFileMixin
201 # On Windows, even though we close the file, we still can't
203 # On Windows, even though we close the file, we still can't
202 # delete it. I have no clue why
204 # delete it. I have no clue why
203 pass
205 pass
204
206
205 def test_histmanager_disabled():
207 def test_histmanager_disabled():
206 """Ensure that disabling the history manager doesn't create a database."""
208 """Ensure that disabling the history manager doesn't create a database."""
207 cfg = Config()
209 cfg = Config()
208 cfg.HistoryAccessor.enabled = False
210 cfg.HistoryAccessor.enabled = False
209
211
210 ip = get_ipython()
212 ip = get_ipython()
211 with TemporaryDirectory() as tmpdir:
213 with TemporaryDirectory() as tmpdir:
212 hist_manager_ori = ip.history_manager
214 hist_manager_ori = ip.history_manager
213 hist_file = Path(tmpdir) / "history.sqlite"
215 hist_file = Path(tmpdir) / "history.sqlite"
214 cfg.HistoryManager.hist_file = hist_file
216 cfg.HistoryManager.hist_file = hist_file
215 try:
217 try:
216 ip.history_manager = HistoryManager(shell=ip, config=cfg)
218 ip.history_manager = HistoryManager(shell=ip, config=cfg)
217 hist = ["a=1", "def f():\n test = 1\n return test", "b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
219 hist = ["a=1", "def f():\n test = 1\n return test", "b='β‚¬Γ†ΒΎΓ·ΓŸ'"]
218 for i, h in enumerate(hist, start=1):
220 for i, h in enumerate(hist, start=1):
219 ip.history_manager.store_inputs(i, h)
221 ip.history_manager.store_inputs(i, h)
220 assert ip.history_manager.input_hist_raw == [""] + hist
222 assert ip.history_manager.input_hist_raw == [""] + hist
221 ip.history_manager.reset()
223 ip.history_manager.reset()
222 ip.history_manager.end_session()
224 ip.history_manager.end_session()
223 finally:
225 finally:
224 ip.history_manager = hist_manager_ori
226 ip.history_manager = hist_manager_ori
225
227
226 # hist_file should not be created
228 # hist_file should not be created
227 assert hist_file.exists() is False
229 assert hist_file.exists() is False
@@ -1,26 +1,27 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test IPython.core.logger"""
2 """Test IPython.core.logger"""
3
3
4 import os.path
4 import os.path
5
5 import pytest
6 import pytest
7 from tempfile import TemporaryDirectory
6
8
7 from IPython.utils.tempdir import TemporaryDirectory
8
9
9 def test_logstart_inaccessible_file():
10 def test_logstart_inaccessible_file():
10 with pytest.raises(IOError):
11 with pytest.raises(IOError):
11 _ip.logger.logstart(logfname="/") # Opening that filename will fail.
12 _ip.logger.logstart(logfname="/") # Opening that filename will fail.
12
13
13 try:
14 try:
14 _ip.run_cell("a=1") # Check it doesn't try to log this
15 _ip.run_cell("a=1") # Check it doesn't try to log this
15 finally:
16 finally:
16 _ip.logger.log_active = False # If this fails, don't let later tests fail
17 _ip.logger.log_active = False # If this fails, don't let later tests fail
17
18
18 def test_logstart_unicode():
19 def test_logstart_unicode():
19 with TemporaryDirectory() as tdir:
20 with TemporaryDirectory() as tdir:
20 logfname = os.path.join(tdir, "test_unicode.log")
21 logfname = os.path.join(tdir, "test_unicode.log")
21 _ip.run_cell("'abc€'")
22 _ip.run_cell("'abc€'")
22 try:
23 try:
23 _ip.magic("logstart -to %s" % logfname)
24 _ip.magic("logstart -to %s" % logfname)
24 _ip.run_cell("'abc€'")
25 _ip.run_cell("'abc€'")
25 finally:
26 finally:
26 _ip.logger.logstop()
27 _ip.logger.logstop()
@@ -1,201 +1,201 b''
1 import errno
1 import errno
2 import os
2 import os
3 import shutil
3 import shutil
4 import sys
4 import sys
5 import tempfile
5 import tempfile
6 import warnings
6 import warnings
7 from unittest.mock import patch
7 from unittest.mock import patch
8
8
9 from testpath import modified_env, assert_isdir, assert_isfile
9 from tempfile import TemporaryDirectory
10 from testpath import assert_isdir, assert_isfile, modified_env
10
11
11 from IPython import paths
12 from IPython import paths
12 from IPython.testing.decorators import skip_win32
13 from IPython.testing.decorators import skip_win32
13 from IPython.utils.tempdir import TemporaryDirectory
14
14
15 TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp())
15 TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp())
16 HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
16 HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
17 XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir")
17 XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir")
18 XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir")
18 XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir")
19 IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython')
19 IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython')
20
20
21 def setup_module():
21 def setup_module():
22 """Setup testenvironment for the module:
22 """Setup testenvironment for the module:
23
23
24 - Adds dummy home dir tree
24 - Adds dummy home dir tree
25 """
25 """
26 # Do not mask exceptions here. In particular, catching WindowsError is a
26 # Do not mask exceptions here. In particular, catching WindowsError is a
27 # problem because that exception is only defined on Windows...
27 # problem because that exception is only defined on Windows...
28 os.makedirs(IP_TEST_DIR)
28 os.makedirs(IP_TEST_DIR)
29 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
29 os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython'))
30 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
30 os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython'))
31
31
32
32
33 def teardown_module():
33 def teardown_module():
34 """Teardown testenvironment for the module:
34 """Teardown testenvironment for the module:
35
35
36 - Remove dummy home dir tree
36 - Remove dummy home dir tree
37 """
37 """
38 # Note: we remove the parent test dir, which is the root of all test
38 # Note: we remove the parent test dir, which is the root of all test
39 # subdirs we may have created. Use shutil instead of os.removedirs, so
39 # subdirs we may have created. Use shutil instead of os.removedirs, so
40 # that non-empty directories are all recursively removed.
40 # that non-empty directories are all recursively removed.
41 shutil.rmtree(TMP_TEST_DIR)
41 shutil.rmtree(TMP_TEST_DIR)
42
42
43 def patch_get_home_dir(dirpath):
43 def patch_get_home_dir(dirpath):
44 return patch.object(paths, 'get_home_dir', return_value=dirpath)
44 return patch.object(paths, 'get_home_dir', return_value=dirpath)
45
45
46
46
47 def test_get_ipython_dir_1():
47 def test_get_ipython_dir_1():
48 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
48 """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions."""
49 env_ipdir = os.path.join("someplace", ".ipython")
49 env_ipdir = os.path.join("someplace", ".ipython")
50 with patch.object(paths, '_writable_dir', return_value=True), \
50 with patch.object(paths, '_writable_dir', return_value=True), \
51 modified_env({'IPYTHONDIR': env_ipdir}):
51 modified_env({'IPYTHONDIR': env_ipdir}):
52 ipdir = paths.get_ipython_dir()
52 ipdir = paths.get_ipython_dir()
53
53
54 assert ipdir == env_ipdir
54 assert ipdir == env_ipdir
55
55
56 def test_get_ipython_dir_2():
56 def test_get_ipython_dir_2():
57 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
57 """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions."""
58 with patch_get_home_dir('someplace'), \
58 with patch_get_home_dir('someplace'), \
59 patch.object(paths, 'get_xdg_dir', return_value=None), \
59 patch.object(paths, 'get_xdg_dir', return_value=None), \
60 patch.object(paths, '_writable_dir', return_value=True), \
60 patch.object(paths, '_writable_dir', return_value=True), \
61 patch('os.name', "posix"), \
61 patch('os.name', "posix"), \
62 modified_env({'IPYTHON_DIR': None,
62 modified_env({'IPYTHON_DIR': None,
63 'IPYTHONDIR': None,
63 'IPYTHONDIR': None,
64 'XDG_CONFIG_HOME': None
64 'XDG_CONFIG_HOME': None
65 }):
65 }):
66 ipdir = paths.get_ipython_dir()
66 ipdir = paths.get_ipython_dir()
67
67
68 assert ipdir == os.path.join("someplace", ".ipython")
68 assert ipdir == os.path.join("someplace", ".ipython")
69
69
70 def test_get_ipython_dir_3():
70 def test_get_ipython_dir_3():
71 """test_get_ipython_dir_3, use XDG if defined and exists, and .ipython doesn't exist."""
71 """test_get_ipython_dir_3, use XDG if defined and exists, and .ipython doesn't exist."""
72 tmphome = TemporaryDirectory()
72 tmphome = TemporaryDirectory()
73 try:
73 try:
74 with patch_get_home_dir(tmphome.name), \
74 with patch_get_home_dir(tmphome.name), \
75 patch('os.name', 'posix'), \
75 patch('os.name', 'posix'), \
76 modified_env({
76 modified_env({
77 'IPYTHON_DIR': None,
77 'IPYTHON_DIR': None,
78 'IPYTHONDIR': None,
78 'IPYTHONDIR': None,
79 'XDG_CONFIG_HOME': XDG_TEST_DIR,
79 'XDG_CONFIG_HOME': XDG_TEST_DIR,
80 }), warnings.catch_warnings(record=True) as w:
80 }), warnings.catch_warnings(record=True) as w:
81 ipdir = paths.get_ipython_dir()
81 ipdir = paths.get_ipython_dir()
82
82
83 assert ipdir == os.path.join(tmphome.name, XDG_TEST_DIR, "ipython")
83 assert ipdir == os.path.join(tmphome.name, XDG_TEST_DIR, "ipython")
84 assert len(w) == 0
84 assert len(w) == 0
85 finally:
85 finally:
86 tmphome.cleanup()
86 tmphome.cleanup()
87
87
88 def test_get_ipython_dir_4():
88 def test_get_ipython_dir_4():
89 """test_get_ipython_dir_4, warn if XDG and home both exist."""
89 """test_get_ipython_dir_4, warn if XDG and home both exist."""
90 with patch_get_home_dir(HOME_TEST_DIR), \
90 with patch_get_home_dir(HOME_TEST_DIR), \
91 patch('os.name', 'posix'):
91 patch('os.name', 'posix'):
92 try:
92 try:
93 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
93 os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython'))
94 except OSError as e:
94 except OSError as e:
95 if e.errno != errno.EEXIST:
95 if e.errno != errno.EEXIST:
96 raise
96 raise
97
97
98
98
99 with modified_env({
99 with modified_env({
100 'IPYTHON_DIR': None,
100 'IPYTHON_DIR': None,
101 'IPYTHONDIR': None,
101 'IPYTHONDIR': None,
102 'XDG_CONFIG_HOME': XDG_TEST_DIR,
102 'XDG_CONFIG_HOME': XDG_TEST_DIR,
103 }), warnings.catch_warnings(record=True) as w:
103 }), warnings.catch_warnings(record=True) as w:
104 ipdir = paths.get_ipython_dir()
104 ipdir = paths.get_ipython_dir()
105
105
106 assert len(w) == 1
106 assert len(w) == 1
107 assert "Ignoring" in str(w[0])
107 assert "Ignoring" in str(w[0])
108
108
109
109
110 def test_get_ipython_dir_5():
110 def test_get_ipython_dir_5():
111 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
111 """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist."""
112 with patch_get_home_dir(HOME_TEST_DIR), \
112 with patch_get_home_dir(HOME_TEST_DIR), \
113 patch('os.name', 'posix'):
113 patch('os.name', 'posix'):
114 try:
114 try:
115 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
115 os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython'))
116 except OSError as e:
116 except OSError as e:
117 if e.errno != errno.ENOENT:
117 if e.errno != errno.ENOENT:
118 raise
118 raise
119
119
120 with modified_env({
120 with modified_env({
121 'IPYTHON_DIR': None,
121 'IPYTHON_DIR': None,
122 'IPYTHONDIR': None,
122 'IPYTHONDIR': None,
123 'XDG_CONFIG_HOME': XDG_TEST_DIR,
123 'XDG_CONFIG_HOME': XDG_TEST_DIR,
124 }):
124 }):
125 ipdir = paths.get_ipython_dir()
125 ipdir = paths.get_ipython_dir()
126
126
127 assert ipdir == IP_TEST_DIR
127 assert ipdir == IP_TEST_DIR
128
128
129 def test_get_ipython_dir_6():
129 def test_get_ipython_dir_6():
130 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
130 """test_get_ipython_dir_6, use home over XDG if defined and neither exist."""
131 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
131 xdg = os.path.join(HOME_TEST_DIR, 'somexdg')
132 os.mkdir(xdg)
132 os.mkdir(xdg)
133 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
133 shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython'))
134 print(paths._writable_dir)
134 print(paths._writable_dir)
135 with patch_get_home_dir(HOME_TEST_DIR), \
135 with patch_get_home_dir(HOME_TEST_DIR), \
136 patch.object(paths, 'get_xdg_dir', return_value=xdg), \
136 patch.object(paths, 'get_xdg_dir', return_value=xdg), \
137 patch('os.name', 'posix'), \
137 patch('os.name', 'posix'), \
138 modified_env({
138 modified_env({
139 'IPYTHON_DIR': None,
139 'IPYTHON_DIR': None,
140 'IPYTHONDIR': None,
140 'IPYTHONDIR': None,
141 'XDG_CONFIG_HOME': None,
141 'XDG_CONFIG_HOME': None,
142 }), warnings.catch_warnings(record=True) as w:
142 }), warnings.catch_warnings(record=True) as w:
143 ipdir = paths.get_ipython_dir()
143 ipdir = paths.get_ipython_dir()
144
144
145 assert ipdir == os.path.join(HOME_TEST_DIR, ".ipython")
145 assert ipdir == os.path.join(HOME_TEST_DIR, ".ipython")
146 assert len(w) == 0
146 assert len(w) == 0
147
147
148 def test_get_ipython_dir_7():
148 def test_get_ipython_dir_7():
149 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
149 """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR"""
150 home_dir = os.path.normpath(os.path.expanduser('~'))
150 home_dir = os.path.normpath(os.path.expanduser('~'))
151 with modified_env({'IPYTHONDIR': os.path.join('~', 'somewhere')}), \
151 with modified_env({'IPYTHONDIR': os.path.join('~', 'somewhere')}), \
152 patch.object(paths, '_writable_dir', return_value=True):
152 patch.object(paths, '_writable_dir', return_value=True):
153 ipdir = paths.get_ipython_dir()
153 ipdir = paths.get_ipython_dir()
154 assert ipdir == os.path.join(home_dir, "somewhere")
154 assert ipdir == os.path.join(home_dir, "somewhere")
155
155
156
156
157 @skip_win32
157 @skip_win32
158 def test_get_ipython_dir_8():
158 def test_get_ipython_dir_8():
159 """test_get_ipython_dir_8, test / home directory"""
159 """test_get_ipython_dir_8, test / home directory"""
160 if not os.access("/", os.W_OK):
160 if not os.access("/", os.W_OK):
161 # test only when HOME directory actually writable
161 # test only when HOME directory actually writable
162 return
162 return
163
163
164 with patch.object(paths, "_writable_dir", lambda path: bool(path)), patch.object(
164 with patch.object(paths, "_writable_dir", lambda path: bool(path)), patch.object(
165 paths, "get_xdg_dir", return_value=None
165 paths, "get_xdg_dir", return_value=None
166 ), modified_env(
166 ), modified_env(
167 {
167 {
168 "IPYTHON_DIR": None,
168 "IPYTHON_DIR": None,
169 "IPYTHONDIR": None,
169 "IPYTHONDIR": None,
170 "HOME": "/",
170 "HOME": "/",
171 }
171 }
172 ):
172 ):
173 assert paths.get_ipython_dir() == "/.ipython"
173 assert paths.get_ipython_dir() == "/.ipython"
174
174
175
175
176 def test_get_ipython_cache_dir():
176 def test_get_ipython_cache_dir():
177 with modified_env({'HOME': HOME_TEST_DIR}):
177 with modified_env({'HOME': HOME_TEST_DIR}):
178 if os.name == "posix":
178 if os.name == "posix":
179 # test default
179 # test default
180 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
180 os.makedirs(os.path.join(HOME_TEST_DIR, ".cache"))
181 with modified_env({'XDG_CACHE_HOME': None}):
181 with modified_env({'XDG_CACHE_HOME': None}):
182 ipdir = paths.get_ipython_cache_dir()
182 ipdir = paths.get_ipython_cache_dir()
183 assert os.path.join(HOME_TEST_DIR, ".cache", "ipython") == ipdir
183 assert os.path.join(HOME_TEST_DIR, ".cache", "ipython") == ipdir
184 assert_isdir(ipdir)
184 assert_isdir(ipdir)
185
185
186 # test env override
186 # test env override
187 with modified_env({"XDG_CACHE_HOME": XDG_CACHE_DIR}):
187 with modified_env({"XDG_CACHE_HOME": XDG_CACHE_DIR}):
188 ipdir = paths.get_ipython_cache_dir()
188 ipdir = paths.get_ipython_cache_dir()
189 assert_isdir(ipdir)
189 assert_isdir(ipdir)
190 assert ipdir == os.path.join(XDG_CACHE_DIR, "ipython")
190 assert ipdir == os.path.join(XDG_CACHE_DIR, "ipython")
191 else:
191 else:
192 assert paths.get_ipython_cache_dir() == paths.get_ipython_dir()
192 assert paths.get_ipython_cache_dir() == paths.get_ipython_dir()
193
193
194 def test_get_ipython_package_dir():
194 def test_get_ipython_package_dir():
195 ipdir = paths.get_ipython_package_dir()
195 ipdir = paths.get_ipython_package_dir()
196 assert_isdir(ipdir)
196 assert_isdir(ipdir)
197
197
198
198
199 def test_get_ipython_module_path():
199 def test_get_ipython_module_path():
200 ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp')
200 ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp')
201 assert_isfile(ipapp_path)
201 assert_isfile(ipapp_path)
@@ -1,156 +1,155 b''
1 # coding: utf-8
1 # coding: utf-8
2 """Tests for profile-related functions.
2 """Tests for profile-related functions.
3
3
4 Currently only the startup-dir functionality is tested, but more tests should
4 Currently only the startup-dir functionality is tested, but more tests should
5 be added for:
5 be added for:
6
6
7 * ipython profile create
7 * ipython profile create
8 * ipython profile list
8 * ipython profile list
9 * ipython profile create --parallel
9 * ipython profile create --parallel
10 * security dir permissions
10 * security dir permissions
11
11
12 Authors
12 Authors
13 -------
13 -------
14
14
15 * MinRK
15 * MinRK
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import shutil
23 import shutil
24 import sys
24 import sys
25 import tempfile
25 import tempfile
26
27 from pathlib import Path
26 from pathlib import Path
28 from unittest import TestCase
27 from unittest import TestCase
29
28
30 from IPython.core.profileapp import list_profiles_in, list_bundled_profiles
29 from tempfile import TemporaryDirectory
31 from IPython.core.profiledir import ProfileDir
32
30
31 from IPython.core.profileapp import list_bundled_profiles, list_profiles_in
32 from IPython.core.profiledir import ProfileDir
33 from IPython.testing import decorators as dec
33 from IPython.testing import decorators as dec
34 from IPython.testing import tools as tt
34 from IPython.testing import tools as tt
35 from IPython.utils.process import getoutput
35 from IPython.utils.process import getoutput
36 from IPython.utils.tempdir import TemporaryDirectory
37
36
38 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
39 # Globals
38 # Globals
40 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
41 TMP_TEST_DIR = Path(tempfile.mkdtemp())
40 TMP_TEST_DIR = Path(tempfile.mkdtemp())
42 HOME_TEST_DIR = TMP_TEST_DIR / "home_test_dir"
41 HOME_TEST_DIR = TMP_TEST_DIR / "home_test_dir"
43 IP_TEST_DIR = HOME_TEST_DIR / ".ipython"
42 IP_TEST_DIR = HOME_TEST_DIR / ".ipython"
44
43
45 #
44 #
46 # Setup/teardown functions/decorators
45 # Setup/teardown functions/decorators
47 #
46 #
48
47
49 def setup_module():
48 def setup_module():
50 """Setup test environment for the module:
49 """Setup test environment for the module:
51
50
52 - Adds dummy home dir tree
51 - Adds dummy home dir tree
53 """
52 """
54 # Do not mask exceptions here. In particular, catching WindowsError is a
53 # Do not mask exceptions here. In particular, catching WindowsError is a
55 # problem because that exception is only defined on Windows...
54 # problem because that exception is only defined on Windows...
56 (Path.cwd() / IP_TEST_DIR).mkdir(parents=True)
55 (Path.cwd() / IP_TEST_DIR).mkdir(parents=True)
57
56
58
57
59 def teardown_module():
58 def teardown_module():
60 """Teardown test environment for the module:
59 """Teardown test environment for the module:
61
60
62 - Remove dummy home dir tree
61 - Remove dummy home dir tree
63 """
62 """
64 # Note: we remove the parent test dir, which is the root of all test
63 # Note: we remove the parent test dir, which is the root of all test
65 # subdirs we may have created. Use shutil instead of os.removedirs, so
64 # subdirs we may have created. Use shutil instead of os.removedirs, so
66 # that non-empty directories are all recursively removed.
65 # that non-empty directories are all recursively removed.
67 shutil.rmtree(TMP_TEST_DIR)
66 shutil.rmtree(TMP_TEST_DIR)
68
67
69
68
70 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
71 # Test functions
70 # Test functions
72 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
73 class ProfileStartupTest(TestCase):
72 class ProfileStartupTest(TestCase):
74 def setUp(self):
73 def setUp(self):
75 # create profile dir
74 # create profile dir
76 self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, "test")
75 self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, "test")
77 self.options = ["--ipython-dir", IP_TEST_DIR, "--profile", "test"]
76 self.options = ["--ipython-dir", IP_TEST_DIR, "--profile", "test"]
78 self.fname = TMP_TEST_DIR / "test.py"
77 self.fname = TMP_TEST_DIR / "test.py"
79
78
80 def tearDown(self):
79 def tearDown(self):
81 # We must remove this profile right away so its presence doesn't
80 # We must remove this profile right away so its presence doesn't
82 # confuse other tests.
81 # confuse other tests.
83 shutil.rmtree(self.pd.location)
82 shutil.rmtree(self.pd.location)
84
83
85 def init(self, startup_file, startup, test):
84 def init(self, startup_file, startup, test):
86 # write startup python file
85 # write startup python file
87 with open(Path(self.pd.startup_dir) / startup_file, "w", encoding="utf-8") as f:
86 with open(Path(self.pd.startup_dir) / startup_file, "w", encoding="utf-8") as f:
88 f.write(startup)
87 f.write(startup)
89 # write simple test file, to check that the startup file was run
88 # write simple test file, to check that the startup file was run
90 with open(self.fname, "w", encoding="utf-8") as f:
89 with open(self.fname, "w", encoding="utf-8") as f:
91 f.write(test)
90 f.write(test)
92
91
93 def validate(self, output):
92 def validate(self, output):
94 tt.ipexec_validate(self.fname, output, "", options=self.options)
93 tt.ipexec_validate(self.fname, output, "", options=self.options)
95
94
96 def test_startup_py(self):
95 def test_startup_py(self):
97 self.init('00-start.py', 'zzz=123\n', 'print(zzz)\n')
96 self.init('00-start.py', 'zzz=123\n', 'print(zzz)\n')
98 self.validate('123')
97 self.validate('123')
99
98
100 def test_startup_ipy(self):
99 def test_startup_ipy(self):
101 self.init('00-start.ipy', '%xmode plain\n', '')
100 self.init('00-start.ipy', '%xmode plain\n', '')
102 self.validate('Exception reporting mode: Plain')
101 self.validate('Exception reporting mode: Plain')
103
102
104
103
105 def test_list_profiles_in():
104 def test_list_profiles_in():
106 # No need to remove these directories and files, as they will get nuked in
105 # No need to remove these directories and files, as they will get nuked in
107 # the module-level teardown.
106 # the module-level teardown.
108 td = Path(tempfile.mkdtemp(dir=TMP_TEST_DIR))
107 td = Path(tempfile.mkdtemp(dir=TMP_TEST_DIR))
109 for name in ("profile_foo", "profile_hello", "not_a_profile"):
108 for name in ("profile_foo", "profile_hello", "not_a_profile"):
110 Path(td / name).mkdir(parents=True)
109 Path(td / name).mkdir(parents=True)
111 if dec.unicode_paths:
110 if dec.unicode_paths:
112 Path(td / u"profile_ΓΌnicode").mkdir(parents=True)
111 Path(td / u"profile_ΓΌnicode").mkdir(parents=True)
113
112
114 with open(td / "profile_file", "w", encoding="utf-8") as f:
113 with open(td / "profile_file", "w", encoding="utf-8") as f:
115 f.write("I am not a profile directory")
114 f.write("I am not a profile directory")
116 profiles = list_profiles_in(td)
115 profiles = list_profiles_in(td)
117
116
118 # unicode normalization can turn u'ΓΌnicode' into u'u\0308nicode',
117 # unicode normalization can turn u'ΓΌnicode' into u'u\0308nicode',
119 # so only check for *nicode, and that creating a ProfileDir from the
118 # so only check for *nicode, and that creating a ProfileDir from the
120 # name remains valid
119 # name remains valid
121 found_unicode = False
120 found_unicode = False
122 for p in list(profiles):
121 for p in list(profiles):
123 if p.endswith('nicode'):
122 if p.endswith('nicode'):
124 pd = ProfileDir.find_profile_dir_by_name(td, p)
123 pd = ProfileDir.find_profile_dir_by_name(td, p)
125 profiles.remove(p)
124 profiles.remove(p)
126 found_unicode = True
125 found_unicode = True
127 break
126 break
128 if dec.unicode_paths:
127 if dec.unicode_paths:
129 assert found_unicode is True
128 assert found_unicode is True
130 assert set(profiles) == {"foo", "hello"}
129 assert set(profiles) == {"foo", "hello"}
131
130
132
131
133 def test_list_bundled_profiles():
132 def test_list_bundled_profiles():
134 # This variable will need to be updated when a new profile gets bundled
133 # This variable will need to be updated when a new profile gets bundled
135 bundled = sorted(list_bundled_profiles())
134 bundled = sorted(list_bundled_profiles())
136 assert bundled == []
135 assert bundled == []
137
136
138
137
139 def test_profile_create_ipython_dir():
138 def test_profile_create_ipython_dir():
140 """ipython profile create respects --ipython-dir"""
139 """ipython profile create respects --ipython-dir"""
141 with TemporaryDirectory() as td:
140 with TemporaryDirectory() as td:
142 getoutput(
141 getoutput(
143 [
142 [
144 sys.executable,
143 sys.executable,
145 "-m",
144 "-m",
146 "IPython",
145 "IPython",
147 "profile",
146 "profile",
148 "create",
147 "create",
149 "foo",
148 "foo",
150 "--ipython-dir=%s" % td,
149 "--ipython-dir=%s" % td,
151 ]
150 ]
152 )
151 )
153 profile_dir = Path(td) / "profile_foo"
152 profile_dir = Path(td) / "profile_foo"
154 assert Path(profile_dir).exists()
153 assert Path(profile_dir).exists()
155 ipython_config = profile_dir / "ipython_config.py"
154 ipython_config = profile_dir / "ipython_config.py"
156 assert Path(ipython_config).exists()
155 assert Path(ipython_config).exists()
@@ -1,620 +1,622 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for code execution (%run and related), which is particularly tricky.
2 """Tests for code execution (%run and related), which is particularly tricky.
3
3
4 Because of how %run manages namespaces, and the fact that we are trying here to
4 Because of how %run manages namespaces, and the fact that we are trying here to
5 verify subtle object deletion and reference counting issues, the %run tests
5 verify subtle object deletion and reference counting issues, the %run tests
6 will be kept in this separate file. This makes it easier to aggregate in one
6 will be kept in this separate file. This makes it easier to aggregate in one
7 place the tricks needed to handle it; most other magics are much easier to test
7 place the tricks needed to handle it; most other magics are much easier to test
8 and we do so in a common test_magic file.
8 and we do so in a common test_magic file.
9
9
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 as otherwise it may influence later tests.
11 as otherwise it may influence later tests.
12 """
12 """
13
13
14 # Copyright (c) IPython Development Team.
14 # Copyright (c) IPython Development Team.
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16
16
17
17
18
18
19 import functools
19 import functools
20 import os
20 import os
21 import platform
21 import platform
22 from os.path import join as pjoin
23 import random
22 import random
24 import string
23 import string
25 import sys
24 import sys
26 import textwrap
25 import textwrap
27 import unittest
26 import unittest
27 from os.path import join as pjoin
28 from unittest.mock import patch
28 from unittest.mock import patch
29
29
30 import pytest
30 import pytest
31 from tempfile import TemporaryDirectory
31
32
33 from IPython.core import debugger
32 from IPython.testing import decorators as dec
34 from IPython.testing import decorators as dec
33 from IPython.testing import tools as tt
35 from IPython.testing import tools as tt
34 from IPython.utils.io import capture_output
36 from IPython.utils.io import capture_output
35 from IPython.utils.tempdir import TemporaryDirectory
37
36 from IPython.core import debugger
37
38
38 def doctest_refbug():
39 def doctest_refbug():
39 """Very nasty problem with references held by multiple runs of a script.
40 """Very nasty problem with references held by multiple runs of a script.
40 See: https://github.com/ipython/ipython/issues/141
41 See: https://github.com/ipython/ipython/issues/141
41
42
42 In [1]: _ip.clear_main_mod_cache()
43 In [1]: _ip.clear_main_mod_cache()
43 # random
44 # random
44
45
45 In [2]: %run refbug
46 In [2]: %run refbug
46
47
47 In [3]: call_f()
48 In [3]: call_f()
48 lowercased: hello
49 lowercased: hello
49
50
50 In [4]: %run refbug
51 In [4]: %run refbug
51
52
52 In [5]: call_f()
53 In [5]: call_f()
53 lowercased: hello
54 lowercased: hello
54 lowercased: hello
55 lowercased: hello
55 """
56 """
56
57
57
58
58 def doctest_run_builtins():
59 def doctest_run_builtins():
59 r"""Check that %run doesn't damage __builtins__.
60 r"""Check that %run doesn't damage __builtins__.
60
61
61 In [1]: import tempfile
62 In [1]: import tempfile
62
63
63 In [2]: bid1 = id(__builtins__)
64 In [2]: bid1 = id(__builtins__)
64
65
65 In [3]: fname = tempfile.mkstemp('.py')[1]
66 In [3]: fname = tempfile.mkstemp('.py')[1]
66
67
67 In [3]: f = open(fname, 'w', encoding='utf-8')
68 In [3]: f = open(fname, 'w', encoding='utf-8')
68
69
69 In [4]: dummy= f.write('pass\n')
70 In [4]: dummy= f.write('pass\n')
70
71
71 In [5]: f.flush()
72 In [5]: f.flush()
72
73
73 In [6]: t1 = type(__builtins__)
74 In [6]: t1 = type(__builtins__)
74
75
75 In [7]: %run $fname
76 In [7]: %run $fname
76
77
77 In [7]: f.close()
78 In [7]: f.close()
78
79
79 In [8]: bid2 = id(__builtins__)
80 In [8]: bid2 = id(__builtins__)
80
81
81 In [9]: t2 = type(__builtins__)
82 In [9]: t2 = type(__builtins__)
82
83
83 In [10]: t1 == t2
84 In [10]: t1 == t2
84 Out[10]: True
85 Out[10]: True
85
86
86 In [10]: bid1 == bid2
87 In [10]: bid1 == bid2
87 Out[10]: True
88 Out[10]: True
88
89
89 In [12]: try:
90 In [12]: try:
90 ....: os.unlink(fname)
91 ....: os.unlink(fname)
91 ....: except:
92 ....: except:
92 ....: pass
93 ....: pass
93 ....:
94 ....:
94 """
95 """
95
96
96
97
97 def doctest_run_option_parser():
98 def doctest_run_option_parser():
98 r"""Test option parser in %run.
99 r"""Test option parser in %run.
99
100
100 In [1]: %run print_argv.py
101 In [1]: %run print_argv.py
101 []
102 []
102
103
103 In [2]: %run print_argv.py print*.py
104 In [2]: %run print_argv.py print*.py
104 ['print_argv.py']
105 ['print_argv.py']
105
106
106 In [3]: %run -G print_argv.py print*.py
107 In [3]: %run -G print_argv.py print*.py
107 ['print*.py']
108 ['print*.py']
108
109
109 """
110 """
110
111
111
112
112 @dec.skip_win32
113 @dec.skip_win32
113 def doctest_run_option_parser_for_posix():
114 def doctest_run_option_parser_for_posix():
114 r"""Test option parser in %run (Linux/OSX specific).
115 r"""Test option parser in %run (Linux/OSX specific).
115
116
116 You need double quote to escape glob in POSIX systems:
117 You need double quote to escape glob in POSIX systems:
117
118
118 In [1]: %run print_argv.py print\\*.py
119 In [1]: %run print_argv.py print\\*.py
119 ['print*.py']
120 ['print*.py']
120
121
121 You can't use quote to escape glob in POSIX systems:
122 You can't use quote to escape glob in POSIX systems:
122
123
123 In [2]: %run print_argv.py 'print*.py'
124 In [2]: %run print_argv.py 'print*.py'
124 ['print_argv.py']
125 ['print_argv.py']
125
126
126 """
127 """
127
128
128
129
129 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
130
131
131
132
132 @dec.skip_if_not_win32
133 @dec.skip_if_not_win32
133 def doctest_run_option_parser_for_windows():
134 def doctest_run_option_parser_for_windows():
134 r"""Test option parser in %run (Windows specific).
135 r"""Test option parser in %run (Windows specific).
135
136
136 In Windows, you can't escape ``*` `by backslash:
137 In Windows, you can't escape ``*` `by backslash:
137
138
138 In [1]: %run print_argv.py print\\*.py
139 In [1]: %run print_argv.py print\\*.py
139 ['print\\\\*.py']
140 ['print\\\\*.py']
140
141
141 You can use quote to escape glob:
142 You can use quote to escape glob:
142
143
143 In [2]: %run print_argv.py 'print*.py'
144 In [2]: %run print_argv.py 'print*.py'
144 ["'print*.py'"]
145 ["'print*.py'"]
145
146
146 """
147 """
147
148
148
149
149 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
150
151
151
152
152 def doctest_reset_del():
153 def doctest_reset_del():
153 """Test that resetting doesn't cause errors in __del__ methods.
154 """Test that resetting doesn't cause errors in __del__ methods.
154
155
155 In [2]: class A(object):
156 In [2]: class A(object):
156 ...: def __del__(self):
157 ...: def __del__(self):
157 ...: print(str("Hi"))
158 ...: print(str("Hi"))
158 ...:
159 ...:
159
160
160 In [3]: a = A()
161 In [3]: a = A()
161
162
162 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
163 Hi
164 Hi
164
165
165 In [5]: 1+1
166 In [5]: 1+1
166 Out[5]: 2
167 Out[5]: 2
167 """
168 """
168
169
169 # For some tests, it will be handy to organize them in a class with a common
170 # For some tests, it will be handy to organize them in a class with a common
170 # setup that makes a temp file
171 # setup that makes a temp file
171
172
172 class TestMagicRunPass(tt.TempFileMixin):
173 class TestMagicRunPass(tt.TempFileMixin):
173
174
174 def setUp(self):
175 def setUp(self):
175 content = "a = [1,2,3]\nb = 1"
176 content = "a = [1,2,3]\nb = 1"
176 self.mktmp(content)
177 self.mktmp(content)
177
178
178 def run_tmpfile(self):
179 def run_tmpfile(self):
179 _ip = get_ipython()
180 _ip = get_ipython()
180 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 _ip.magic('run %s' % self.fname)
183 _ip.magic('run %s' % self.fname)
183
184
184 def run_tmpfile_p(self):
185 def run_tmpfile_p(self):
185 _ip = get_ipython()
186 _ip = get_ipython()
186 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
187 # See below and ticket https://bugs.launchpad.net/bugs/366353
188 # See below and ticket https://bugs.launchpad.net/bugs/366353
188 _ip.magic('run -p %s' % self.fname)
189 _ip.magic('run -p %s' % self.fname)
189
190
190 def test_builtins_id(self):
191 def test_builtins_id(self):
191 """Check that %run doesn't damage __builtins__ """
192 """Check that %run doesn't damage __builtins__ """
192 _ip = get_ipython()
193 _ip = get_ipython()
193 # Test that the id of __builtins__ is not modified by %run
194 # Test that the id of __builtins__ is not modified by %run
194 bid1 = id(_ip.user_ns['__builtins__'])
195 bid1 = id(_ip.user_ns['__builtins__'])
195 self.run_tmpfile()
196 self.run_tmpfile()
196 bid2 = id(_ip.user_ns['__builtins__'])
197 bid2 = id(_ip.user_ns['__builtins__'])
197 assert bid1 == bid2
198 assert bid1 == bid2
198
199
199 def test_builtins_type(self):
200 def test_builtins_type(self):
200 """Check that the type of __builtins__ doesn't change with %run.
201 """Check that the type of __builtins__ doesn't change with %run.
201
202
202 However, the above could pass if __builtins__ was already modified to
203 However, the above could pass if __builtins__ was already modified to
203 be a dict (it should be a module) by a previous use of %run. So we
204 be a dict (it should be a module) by a previous use of %run. So we
204 also check explicitly that it really is a module:
205 also check explicitly that it really is a module:
205 """
206 """
206 _ip = get_ipython()
207 _ip = get_ipython()
207 self.run_tmpfile()
208 self.run_tmpfile()
208 assert type(_ip.user_ns["__builtins__"]) == type(sys)
209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
209
210
210 def test_run_profile(self):
211 def test_run_profile(self):
211 """Test that the option -p, which invokes the profiler, do not
212 """Test that the option -p, which invokes the profiler, do not
212 crash by invoking execfile"""
213 crash by invoking execfile"""
213 self.run_tmpfile_p()
214 self.run_tmpfile_p()
214
215
215 def test_run_debug_twice(self):
216 def test_run_debug_twice(self):
216 # https://github.com/ipython/ipython/issues/10028
217 # https://github.com/ipython/ipython/issues/10028
217 _ip = get_ipython()
218 _ip = get_ipython()
218 with tt.fake_input(['c']):
219 with tt.fake_input(['c']):
219 _ip.magic('run -d %s' % self.fname)
220 _ip.magic('run -d %s' % self.fname)
220 with tt.fake_input(['c']):
221 with tt.fake_input(['c']):
221 _ip.magic('run -d %s' % self.fname)
222 _ip.magic('run -d %s' % self.fname)
222
223
223 def test_run_debug_twice_with_breakpoint(self):
224 def test_run_debug_twice_with_breakpoint(self):
224 """Make a valid python temp file."""
225 """Make a valid python temp file."""
225 _ip = get_ipython()
226 _ip = get_ipython()
226 with tt.fake_input(['b 2', 'c', 'c']):
227 with tt.fake_input(['b 2', 'c', 'c']):
227 _ip.magic('run -d %s' % self.fname)
228 _ip.magic('run -d %s' % self.fname)
228
229
229 with tt.fake_input(['c']):
230 with tt.fake_input(['c']):
230 with tt.AssertNotPrints('KeyError'):
231 with tt.AssertNotPrints('KeyError'):
231 _ip.magic('run -d %s' % self.fname)
232 _ip.magic('run -d %s' % self.fname)
232
233
233
234
234 class TestMagicRunSimple(tt.TempFileMixin):
235 class TestMagicRunSimple(tt.TempFileMixin):
235
236
236 def test_simpledef(self):
237 def test_simpledef(self):
237 """Test that simple class definitions work."""
238 """Test that simple class definitions work."""
238 src = ("class foo: pass\n"
239 src = ("class foo: pass\n"
239 "def f(): return foo()")
240 "def f(): return foo()")
240 self.mktmp(src)
241 self.mktmp(src)
241 _ip.magic("run %s" % self.fname)
242 _ip.magic("run %s" % self.fname)
242 _ip.run_cell("t = isinstance(f(), foo)")
243 _ip.run_cell("t = isinstance(f(), foo)")
243 assert _ip.user_ns["t"] is True
244 assert _ip.user_ns["t"] is True
244
245
245 @pytest.mark.xfail(
246 @pytest.mark.xfail(
246 platform.python_implementation() == "PyPy",
247 platform.python_implementation() == "PyPy",
247 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
248 )
249 )
249 def test_obj_del(self):
250 def test_obj_del(self):
250 """Test that object's __del__ methods are called on exit."""
251 """Test that object's __del__ methods are called on exit."""
251 src = ("class A(object):\n"
252 src = ("class A(object):\n"
252 " def __del__(self):\n"
253 " def __del__(self):\n"
253 " print('object A deleted')\n"
254 " print('object A deleted')\n"
254 "a = A()\n")
255 "a = A()\n")
255 self.mktmp(src)
256 self.mktmp(src)
256 err = None
257 err = None
257 tt.ipexec_validate(self.fname, 'object A deleted', err)
258 tt.ipexec_validate(self.fname, 'object A deleted', err)
258
259
259 def test_aggressive_namespace_cleanup(self):
260 def test_aggressive_namespace_cleanup(self):
260 """Test that namespace cleanup is not too aggressive GH-238
261 """Test that namespace cleanup is not too aggressive GH-238
261
262
262 Returning from another run magic deletes the namespace"""
263 Returning from another run magic deletes the namespace"""
263 # see ticket https://github.com/ipython/ipython/issues/238
264 # see ticket https://github.com/ipython/ipython/issues/238
264
265
265 with tt.TempFileMixin() as empty:
266 with tt.TempFileMixin() as empty:
266 empty.mktmp("")
267 empty.mktmp("")
267 # On Windows, the filename will have \users in it, so we need to use the
268 # On Windows, the filename will have \users in it, so we need to use the
268 # repr so that the \u becomes \\u.
269 # repr so that the \u becomes \\u.
269 src = (
270 src = (
270 "ip = get_ipython()\n"
271 "ip = get_ipython()\n"
271 "for i in range(5):\n"
272 "for i in range(5):\n"
272 " try:\n"
273 " try:\n"
273 " ip.magic(%r)\n"
274 " ip.magic(%r)\n"
274 " except NameError as e:\n"
275 " except NameError as e:\n"
275 " print(i)\n"
276 " print(i)\n"
276 " break\n" % ("run " + empty.fname)
277 " break\n" % ("run " + empty.fname)
277 )
278 )
278 self.mktmp(src)
279 self.mktmp(src)
279 _ip.magic("run %s" % self.fname)
280 _ip.magic("run %s" % self.fname)
280 _ip.run_cell("ip == get_ipython()")
281 _ip.run_cell("ip == get_ipython()")
281 assert _ip.user_ns["i"] == 4
282 assert _ip.user_ns["i"] == 4
282
283
283 def test_run_second(self):
284 def test_run_second(self):
284 """Test that running a second file doesn't clobber the first, gh-3547"""
285 """Test that running a second file doesn't clobber the first, gh-3547"""
285 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
286
287
287 with tt.TempFileMixin() as empty:
288 with tt.TempFileMixin() as empty:
288 empty.mktmp("")
289 empty.mktmp("")
289
290
290 _ip.magic("run %s" % self.fname)
291 _ip.magic("run %s" % self.fname)
291 _ip.magic("run %s" % empty.fname)
292 _ip.magic("run %s" % empty.fname)
292 assert _ip.user_ns["afunc"]() == 1
293 assert _ip.user_ns["afunc"]() == 1
293
294
294 def test_tclass(self):
295 def test_tclass(self):
295 mydir = os.path.dirname(__file__)
296 mydir = os.path.dirname(__file__)
296 tc = os.path.join(mydir, "tclass")
297 tc = os.path.join(mydir, "tclass")
297 src = f"""\
298 src = f"""\
298 import gc
299 import gc
299 %run "{tc}" C-first
300 %run "{tc}" C-first
300 gc.collect(0)
301 gc.collect(0)
301 %run "{tc}" C-second
302 %run "{tc}" C-second
302 gc.collect(0)
303 gc.collect(0)
303 %run "{tc}" C-third
304 %run "{tc}" C-third
304 gc.collect(0)
305 gc.collect(0)
305 %reset -f
306 %reset -f
306 """
307 """
307 self.mktmp(src, ".ipy")
308 self.mktmp(src, ".ipy")
308 out = """\
309 out = """\
309 ARGV 1-: ['C-first']
310 ARGV 1-: ['C-first']
310 ARGV 1-: ['C-second']
311 ARGV 1-: ['C-second']
311 tclass.py: deleting object: C-first
312 tclass.py: deleting object: C-first
312 ARGV 1-: ['C-third']
313 ARGV 1-: ['C-third']
313 tclass.py: deleting object: C-second
314 tclass.py: deleting object: C-second
314 tclass.py: deleting object: C-third
315 tclass.py: deleting object: C-third
315 """
316 """
316 err = None
317 err = None
317 tt.ipexec_validate(self.fname, out, err)
318 tt.ipexec_validate(self.fname, out, err)
318
319
319 def test_run_i_after_reset(self):
320 def test_run_i_after_reset(self):
320 """Check that %run -i still works after %reset (gh-693)"""
321 """Check that %run -i still works after %reset (gh-693)"""
321 src = "yy = zz\n"
322 src = "yy = zz\n"
322 self.mktmp(src)
323 self.mktmp(src)
323 _ip.run_cell("zz = 23")
324 _ip.run_cell("zz = 23")
324 try:
325 try:
325 _ip.magic("run -i %s" % self.fname)
326 _ip.magic("run -i %s" % self.fname)
326 assert _ip.user_ns["yy"] == 23
327 assert _ip.user_ns["yy"] == 23
327 finally:
328 finally:
328 _ip.magic('reset -f')
329 _ip.magic('reset -f')
329
330
330 _ip.run_cell("zz = 23")
331 _ip.run_cell("zz = 23")
331 try:
332 try:
332 _ip.magic("run -i %s" % self.fname)
333 _ip.magic("run -i %s" % self.fname)
333 assert _ip.user_ns["yy"] == 23
334 assert _ip.user_ns["yy"] == 23
334 finally:
335 finally:
335 _ip.magic('reset -f')
336 _ip.magic('reset -f')
336
337
337 def test_unicode(self):
338 def test_unicode(self):
338 """Check that files in odd encodings are accepted."""
339 """Check that files in odd encodings are accepted."""
339 mydir = os.path.dirname(__file__)
340 mydir = os.path.dirname(__file__)
340 na = os.path.join(mydir, 'nonascii.py')
341 na = os.path.join(mydir, 'nonascii.py')
341 _ip.magic('run "%s"' % na)
342 _ip.magic('run "%s"' % na)
342 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
343
344
344 def test_run_py_file_attribute(self):
345 def test_run_py_file_attribute(self):
345 """Test handling of `__file__` attribute in `%run <file>.py`."""
346 """Test handling of `__file__` attribute in `%run <file>.py`."""
346 src = "t = __file__\n"
347 src = "t = __file__\n"
347 self.mktmp(src)
348 self.mktmp(src)
348 _missing = object()
349 _missing = object()
349 file1 = _ip.user_ns.get('__file__', _missing)
350 file1 = _ip.user_ns.get('__file__', _missing)
350 _ip.magic('run %s' % self.fname)
351 _ip.magic('run %s' % self.fname)
351 file2 = _ip.user_ns.get('__file__', _missing)
352 file2 = _ip.user_ns.get('__file__', _missing)
352
353
353 # Check that __file__ was equal to the filename in the script's
354 # Check that __file__ was equal to the filename in the script's
354 # namespace.
355 # namespace.
355 assert _ip.user_ns["t"] == self.fname
356 assert _ip.user_ns["t"] == self.fname
356
357
357 # Check that __file__ was not leaked back into user_ns.
358 # Check that __file__ was not leaked back into user_ns.
358 assert file1 == file2
359 assert file1 == file2
359
360
360 def test_run_ipy_file_attribute(self):
361 def test_run_ipy_file_attribute(self):
361 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
362 src = "t = __file__\n"
363 src = "t = __file__\n"
363 self.mktmp(src, ext='.ipy')
364 self.mktmp(src, ext='.ipy')
364 _missing = object()
365 _missing = object()
365 file1 = _ip.user_ns.get('__file__', _missing)
366 file1 = _ip.user_ns.get('__file__', _missing)
366 _ip.magic('run %s' % self.fname)
367 _ip.magic('run %s' % self.fname)
367 file2 = _ip.user_ns.get('__file__', _missing)
368 file2 = _ip.user_ns.get('__file__', _missing)
368
369
369 # Check that __file__ was equal to the filename in the script's
370 # Check that __file__ was equal to the filename in the script's
370 # namespace.
371 # namespace.
371 assert _ip.user_ns["t"] == self.fname
372 assert _ip.user_ns["t"] == self.fname
372
373
373 # Check that __file__ was not leaked back into user_ns.
374 # Check that __file__ was not leaked back into user_ns.
374 assert file1 == file2
375 assert file1 == file2
375
376
376 def test_run_formatting(self):
377 def test_run_formatting(self):
377 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
378 src = "pass"
379 src = "pass"
379 self.mktmp(src)
380 self.mktmp(src)
380 _ip.magic('run -t -N 1 %s' % self.fname)
381 _ip.magic('run -t -N 1 %s' % self.fname)
381 _ip.magic('run -t -N 10 %s' % self.fname)
382 _ip.magic('run -t -N 10 %s' % self.fname)
382
383
383 def test_ignore_sys_exit(self):
384 def test_ignore_sys_exit(self):
384 """Test the -e option to ignore sys.exit()"""
385 """Test the -e option to ignore sys.exit()"""
385 src = "import sys; sys.exit(1)"
386 src = "import sys; sys.exit(1)"
386 self.mktmp(src)
387 self.mktmp(src)
387 with tt.AssertPrints('SystemExit'):
388 with tt.AssertPrints('SystemExit'):
388 _ip.magic('run %s' % self.fname)
389 _ip.magic('run %s' % self.fname)
389
390
390 with tt.AssertNotPrints('SystemExit'):
391 with tt.AssertNotPrints('SystemExit'):
391 _ip.magic('run -e %s' % self.fname)
392 _ip.magic('run -e %s' % self.fname)
392
393
393 def test_run_nb(self):
394 def test_run_nb(self):
394 """Test %run notebook.ipynb"""
395 """Test %run notebook.ipynb"""
395 pytest.importorskip("nbformat")
396 pytest.importorskip("nbformat")
396 from nbformat import v4, writes
397 from nbformat import v4, writes
397 nb = v4.new_notebook(
398 nb = v4.new_notebook(
398 cells=[
399 cells=[
399 v4.new_markdown_cell("The Ultimate Question of Everything"),
400 v4.new_markdown_cell("The Ultimate Question of Everything"),
400 v4.new_code_cell("answer=42")
401 v4.new_code_cell("answer=42")
401 ]
402 ]
402 )
403 )
403 src = writes(nb, version=4)
404 src = writes(nb, version=4)
404 self.mktmp(src, ext='.ipynb')
405 self.mktmp(src, ext='.ipynb')
405
406
406 _ip.magic("run %s" % self.fname)
407 _ip.magic("run %s" % self.fname)
407
408
408 assert _ip.user_ns["answer"] == 42
409 assert _ip.user_ns["answer"] == 42
409
410
410 def test_run_nb_error(self):
411 def test_run_nb_error(self):
411 """Test %run notebook.ipynb error"""
412 """Test %run notebook.ipynb error"""
412 pytest.importorskip("nbformat")
413 pytest.importorskip("nbformat")
413 from nbformat import v4, writes
414 from nbformat import v4, writes
415
414 # %run when a file name isn't provided
416 # %run when a file name isn't provided
415 pytest.raises(Exception, _ip.magic, "run")
417 pytest.raises(Exception, _ip.magic, "run")
416
418
417 # %run when a file doesn't exist
419 # %run when a file doesn't exist
418 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
419
421
420 # %run on a notebook with an error
422 # %run on a notebook with an error
421 nb = v4.new_notebook(
423 nb = v4.new_notebook(
422 cells=[
424 cells=[
423 v4.new_code_cell("0/0")
425 v4.new_code_cell("0/0")
424 ]
426 ]
425 )
427 )
426 src = writes(nb, version=4)
428 src = writes(nb, version=4)
427 self.mktmp(src, ext='.ipynb')
429 self.mktmp(src, ext='.ipynb')
428 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
429
431
430 def test_file_options(self):
432 def test_file_options(self):
431 src = ('import sys\n'
433 src = ('import sys\n'
432 'a = " ".join(sys.argv[1:])\n')
434 'a = " ".join(sys.argv[1:])\n')
433 self.mktmp(src)
435 self.mktmp(src)
434 test_opts = "-x 3 --verbose"
436 test_opts = "-x 3 --verbose"
435 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
436 assert _ip.user_ns["a"] == test_opts
438 assert _ip.user_ns["a"] == test_opts
437
439
438
440
439 class TestMagicRunWithPackage(unittest.TestCase):
441 class TestMagicRunWithPackage(unittest.TestCase):
440
442
441 def writefile(self, name, content):
443 def writefile(self, name, content):
442 path = os.path.join(self.tempdir.name, name)
444 path = os.path.join(self.tempdir.name, name)
443 d = os.path.dirname(path)
445 d = os.path.dirname(path)
444 if not os.path.isdir(d):
446 if not os.path.isdir(d):
445 os.makedirs(d)
447 os.makedirs(d)
446 with open(path, "w", encoding="utf-8") as f:
448 with open(path, "w", encoding="utf-8") as f:
447 f.write(textwrap.dedent(content))
449 f.write(textwrap.dedent(content))
448
450
449 def setUp(self):
451 def setUp(self):
450 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
451 """Temporary (probably) valid python package name."""
453 """Temporary (probably) valid python package name."""
452
454
453 self.value = int(random.random() * 10000)
455 self.value = int(random.random() * 10000)
454
456
455 self.tempdir = TemporaryDirectory()
457 self.tempdir = TemporaryDirectory()
456 self.__orig_cwd = os.getcwd()
458 self.__orig_cwd = os.getcwd()
457 sys.path.insert(0, self.tempdir.name)
459 sys.path.insert(0, self.tempdir.name)
458
460
459 self.writefile(os.path.join(package, '__init__.py'), '')
461 self.writefile(os.path.join(package, '__init__.py'), '')
460 self.writefile(os.path.join(package, 'sub.py'), """
462 self.writefile(os.path.join(package, 'sub.py'), """
461 x = {0!r}
463 x = {0!r}
462 """.format(self.value))
464 """.format(self.value))
463 self.writefile(os.path.join(package, 'relative.py'), """
465 self.writefile(os.path.join(package, 'relative.py'), """
464 from .sub import x
466 from .sub import x
465 """)
467 """)
466 self.writefile(os.path.join(package, 'absolute.py'), """
468 self.writefile(os.path.join(package, 'absolute.py'), """
467 from {0}.sub import x
469 from {0}.sub import x
468 """.format(package))
470 """.format(package))
469 self.writefile(os.path.join(package, 'args.py'), """
471 self.writefile(os.path.join(package, 'args.py'), """
470 import sys
472 import sys
471 a = " ".join(sys.argv[1:])
473 a = " ".join(sys.argv[1:])
472 """.format(package))
474 """.format(package))
473
475
474 def tearDown(self):
476 def tearDown(self):
475 os.chdir(self.__orig_cwd)
477 os.chdir(self.__orig_cwd)
476 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
477 self.tempdir.cleanup()
479 self.tempdir.cleanup()
478
480
479 def check_run_submodule(self, submodule, opts=''):
481 def check_run_submodule(self, submodule, opts=''):
480 _ip.user_ns.pop('x', None)
482 _ip.user_ns.pop('x', None)
481 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
483 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
482 self.assertEqual(_ip.user_ns['x'], self.value,
484 self.assertEqual(_ip.user_ns['x'], self.value,
483 'Variable `x` is not loaded from module `{0}`.'
485 'Variable `x` is not loaded from module `{0}`.'
484 .format(submodule))
486 .format(submodule))
485
487
486 def test_run_submodule_with_absolute_import(self):
488 def test_run_submodule_with_absolute_import(self):
487 self.check_run_submodule('absolute')
489 self.check_run_submodule('absolute')
488
490
489 def test_run_submodule_with_relative_import(self):
491 def test_run_submodule_with_relative_import(self):
490 """Run submodule that has a relative import statement (#2727)."""
492 """Run submodule that has a relative import statement (#2727)."""
491 self.check_run_submodule('relative')
493 self.check_run_submodule('relative')
492
494
493 def test_prun_submodule_with_absolute_import(self):
495 def test_prun_submodule_with_absolute_import(self):
494 self.check_run_submodule('absolute', '-p')
496 self.check_run_submodule('absolute', '-p')
495
497
496 def test_prun_submodule_with_relative_import(self):
498 def test_prun_submodule_with_relative_import(self):
497 self.check_run_submodule('relative', '-p')
499 self.check_run_submodule('relative', '-p')
498
500
499 def with_fake_debugger(func):
501 def with_fake_debugger(func):
500 @functools.wraps(func)
502 @functools.wraps(func)
501 def wrapper(*args, **kwds):
503 def wrapper(*args, **kwds):
502 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
504 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
503 return func(*args, **kwds)
505 return func(*args, **kwds)
504 return wrapper
506 return wrapper
505
507
506 @with_fake_debugger
508 @with_fake_debugger
507 def test_debug_run_submodule_with_absolute_import(self):
509 def test_debug_run_submodule_with_absolute_import(self):
508 self.check_run_submodule('absolute', '-d')
510 self.check_run_submodule('absolute', '-d')
509
511
510 @with_fake_debugger
512 @with_fake_debugger
511 def test_debug_run_submodule_with_relative_import(self):
513 def test_debug_run_submodule_with_relative_import(self):
512 self.check_run_submodule('relative', '-d')
514 self.check_run_submodule('relative', '-d')
513
515
514 def test_module_options(self):
516 def test_module_options(self):
515 _ip.user_ns.pop("a", None)
517 _ip.user_ns.pop("a", None)
516 test_opts = "-x abc -m test"
518 test_opts = "-x abc -m test"
517 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
519 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
518 assert _ip.user_ns["a"] == test_opts
520 assert _ip.user_ns["a"] == test_opts
519
521
520 def test_module_options_with_separator(self):
522 def test_module_options_with_separator(self):
521 _ip.user_ns.pop("a", None)
523 _ip.user_ns.pop("a", None)
522 test_opts = "-x abc -m test"
524 test_opts = "-x abc -m test"
523 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
525 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
524 assert _ip.user_ns["a"] == test_opts
526 assert _ip.user_ns["a"] == test_opts
525
527
526
528
527 def test_run__name__():
529 def test_run__name__():
528 with TemporaryDirectory() as td:
530 with TemporaryDirectory() as td:
529 path = pjoin(td, "foo.py")
531 path = pjoin(td, "foo.py")
530 with open(path, "w", encoding="utf-8") as f:
532 with open(path, "w", encoding="utf-8") as f:
531 f.write("q = __name__")
533 f.write("q = __name__")
532
534
533 _ip.user_ns.pop("q", None)
535 _ip.user_ns.pop("q", None)
534 _ip.magic("run {}".format(path))
536 _ip.magic("run {}".format(path))
535 assert _ip.user_ns.pop("q") == "__main__"
537 assert _ip.user_ns.pop("q") == "__main__"
536
538
537 _ip.magic("run -n {}".format(path))
539 _ip.magic("run -n {}".format(path))
538 assert _ip.user_ns.pop("q") == "foo"
540 assert _ip.user_ns.pop("q") == "foo"
539
541
540 try:
542 try:
541 _ip.magic("run -i -n {}".format(path))
543 _ip.magic("run -i -n {}".format(path))
542 assert _ip.user_ns.pop("q") == "foo"
544 assert _ip.user_ns.pop("q") == "foo"
543 finally:
545 finally:
544 _ip.magic('reset -f')
546 _ip.magic('reset -f')
545
547
546
548
547 def test_run_tb():
549 def test_run_tb():
548 """Test traceback offset in %run"""
550 """Test traceback offset in %run"""
549 with TemporaryDirectory() as td:
551 with TemporaryDirectory() as td:
550 path = pjoin(td, "foo.py")
552 path = pjoin(td, "foo.py")
551 with open(path, "w", encoding="utf-8") as f:
553 with open(path, "w", encoding="utf-8") as f:
552 f.write(
554 f.write(
553 "\n".join(
555 "\n".join(
554 [
556 [
555 "def foo():",
557 "def foo():",
556 " return bar()",
558 " return bar()",
557 "def bar():",
559 "def bar():",
558 " raise RuntimeError('hello!')",
560 " raise RuntimeError('hello!')",
559 "foo()",
561 "foo()",
560 ]
562 ]
561 )
563 )
562 )
564 )
563 with capture_output() as io:
565 with capture_output() as io:
564 _ip.magic('run {}'.format(path))
566 _ip.magic('run {}'.format(path))
565 out = io.stdout
567 out = io.stdout
566 assert "execfile" not in out
568 assert "execfile" not in out
567 assert "RuntimeError" in out
569 assert "RuntimeError" in out
568 assert out.count("---->") == 3
570 assert out.count("---->") == 3
569 del ip.user_ns['bar']
571 del ip.user_ns['bar']
570 del ip.user_ns['foo']
572 del ip.user_ns['foo']
571
573
572
574
573 def test_multiprocessing_run():
575 def test_multiprocessing_run():
574 """Set we can run mutiprocesgin without messing up up main namespace
576 """Set we can run mutiprocesgin without messing up up main namespace
575
577
576 Note that import `nose.tools as nt` mdify the value s
578 Note that import `nose.tools as nt` mdify the value s
577 sys.module['__mp_main__'] so we need to temporarily set it to None to test
579 sys.module['__mp_main__'] so we need to temporarily set it to None to test
578 the issue.
580 the issue.
579 """
581 """
580 with TemporaryDirectory() as td:
582 with TemporaryDirectory() as td:
581 mpm = sys.modules.get('__mp_main__')
583 mpm = sys.modules.get('__mp_main__')
582 sys.modules['__mp_main__'] = None
584 sys.modules['__mp_main__'] = None
583 try:
585 try:
584 path = pjoin(td, "test.py")
586 path = pjoin(td, "test.py")
585 with open(path, "w", encoding="utf-8") as f:
587 with open(path, "w", encoding="utf-8") as f:
586 f.write("import multiprocessing\nprint('hoy')")
588 f.write("import multiprocessing\nprint('hoy')")
587 with capture_output() as io:
589 with capture_output() as io:
588 _ip.run_line_magic('run', path)
590 _ip.run_line_magic('run', path)
589 _ip.run_cell("i_m_undefined")
591 _ip.run_cell("i_m_undefined")
590 out = io.stdout
592 out = io.stdout
591 assert "hoy" in out
593 assert "hoy" in out
592 assert "AttributeError" not in out
594 assert "AttributeError" not in out
593 assert "NameError" in out
595 assert "NameError" in out
594 assert out.count("---->") == 1
596 assert out.count("---->") == 1
595 except:
597 except:
596 raise
598 raise
597 finally:
599 finally:
598 sys.modules['__mp_main__'] = mpm
600 sys.modules['__mp_main__'] = mpm
599
601
600
602
601 def test_script_tb():
603 def test_script_tb():
602 """Test traceback offset in `ipython script.py`"""
604 """Test traceback offset in `ipython script.py`"""
603 with TemporaryDirectory() as td:
605 with TemporaryDirectory() as td:
604 path = pjoin(td, "foo.py")
606 path = pjoin(td, "foo.py")
605 with open(path, "w", encoding="utf-8") as f:
607 with open(path, "w", encoding="utf-8") as f:
606 f.write(
608 f.write(
607 "\n".join(
609 "\n".join(
608 [
610 [
609 "def foo():",
611 "def foo():",
610 " return bar()",
612 " return bar()",
611 "def bar():",
613 "def bar():",
612 " raise RuntimeError('hello!')",
614 " raise RuntimeError('hello!')",
613 "foo()",
615 "foo()",
614 ]
616 ]
615 )
617 )
616 )
618 )
617 out, err = tt.ipexec(path)
619 out, err = tt.ipexec(path)
618 assert "execfile" not in out
620 assert "execfile" not in out
619 assert "RuntimeError" in out
621 assert "RuntimeError" in out
620 assert out.count("---->") == 3
622 assert out.count("---->") == 3
@@ -1,410 +1,409 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import logging
5 import logging
6 import os.path
6 import platform
7 import platform
7 import re
8 import re
8 import sys
9 import sys
9 import os.path
10 from textwrap import dedent
11 import traceback
10 import traceback
12 import unittest
11 import unittest
12 from textwrap import dedent
13
13
14 from IPython.core.ultratb import ColorTB, VerboseTB
14 from tempfile import TemporaryDirectory
15
16
15
16 from IPython.core.ultratb import ColorTB, VerboseTB
17 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
18 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.testing.decorators import onlyif_unicode_paths
19 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.syspathcontext import prepended_to_syspath
20 from IPython.utils.tempdir import TemporaryDirectory
21
20
22 file_1 = """1
21 file_1 = """1
23 2
22 2
24 3
23 3
25 def f():
24 def f():
26 1/0
25 1/0
27 """
26 """
28
27
29 file_2 = """def f():
28 file_2 = """def f():
30 1/0
29 1/0
31 """
30 """
32
31
33
32
34 def recursionlimit(frames):
33 def recursionlimit(frames):
35 """
34 """
36 decorator to set the recursion limit temporarily
35 decorator to set the recursion limit temporarily
37 """
36 """
38
37
39 def inner(test_function):
38 def inner(test_function):
40 def wrapper(*args, **kwargs):
39 def wrapper(*args, **kwargs):
41 rl = sys.getrecursionlimit()
40 rl = sys.getrecursionlimit()
42 sys.setrecursionlimit(frames)
41 sys.setrecursionlimit(frames)
43 try:
42 try:
44 return test_function(*args, **kwargs)
43 return test_function(*args, **kwargs)
45 finally:
44 finally:
46 sys.setrecursionlimit(rl)
45 sys.setrecursionlimit(rl)
47
46
48 return wrapper
47 return wrapper
49
48
50 return inner
49 return inner
51
50
52
51
53 class ChangedPyFileTest(unittest.TestCase):
52 class ChangedPyFileTest(unittest.TestCase):
54 def test_changing_py_file(self):
53 def test_changing_py_file(self):
55 """Traceback produced if the line where the error occurred is missing?
54 """Traceback produced if the line where the error occurred is missing?
56
55
57 https://github.com/ipython/ipython/issues/1456
56 https://github.com/ipython/ipython/issues/1456
58 """
57 """
59 with TemporaryDirectory() as td:
58 with TemporaryDirectory() as td:
60 fname = os.path.join(td, "foo.py")
59 fname = os.path.join(td, "foo.py")
61 with open(fname, "w", encoding="utf-8") as f:
60 with open(fname, "w", encoding="utf-8") as f:
62 f.write(file_1)
61 f.write(file_1)
63
62
64 with prepended_to_syspath(td):
63 with prepended_to_syspath(td):
65 ip.run_cell("import foo")
64 ip.run_cell("import foo")
66
65
67 with tt.AssertPrints("ZeroDivisionError"):
66 with tt.AssertPrints("ZeroDivisionError"):
68 ip.run_cell("foo.f()")
67 ip.run_cell("foo.f()")
69
68
70 # Make the file shorter, so the line of the error is missing.
69 # Make the file shorter, so the line of the error is missing.
71 with open(fname, "w", encoding="utf-8") as f:
70 with open(fname, "w", encoding="utf-8") as f:
72 f.write(file_2)
71 f.write(file_2)
73
72
74 # For some reason, this was failing on the *second* call after
73 # For some reason, this was failing on the *second* call after
75 # changing the file, so we call f() twice.
74 # changing the file, so we call f() twice.
76 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
75 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
77 with tt.AssertPrints("ZeroDivisionError"):
76 with tt.AssertPrints("ZeroDivisionError"):
78 ip.run_cell("foo.f()")
77 ip.run_cell("foo.f()")
79 with tt.AssertPrints("ZeroDivisionError"):
78 with tt.AssertPrints("ZeroDivisionError"):
80 ip.run_cell("foo.f()")
79 ip.run_cell("foo.f()")
81
80
82 iso_8859_5_file = u'''# coding: iso-8859-5
81 iso_8859_5_file = u'''# coding: iso-8859-5
83
82
84 def fail():
83 def fail():
85 """Π΄Π±Π˜Π–"""
84 """Π΄Π±Π˜Π–"""
86 1/0 # Π΄Π±Π˜Π–
85 1/0 # Π΄Π±Π˜Π–
87 '''
86 '''
88
87
89 class NonAsciiTest(unittest.TestCase):
88 class NonAsciiTest(unittest.TestCase):
90 @onlyif_unicode_paths
89 @onlyif_unicode_paths
91 def test_nonascii_path(self):
90 def test_nonascii_path(self):
92 # Non-ascii directory name as well.
91 # Non-ascii directory name as well.
93 with TemporaryDirectory(suffix=u'Γ©') as td:
92 with TemporaryDirectory(suffix=u'Γ©') as td:
94 fname = os.path.join(td, u"fooΓ©.py")
93 fname = os.path.join(td, u"fooΓ©.py")
95 with open(fname, "w", encoding="utf-8") as f:
94 with open(fname, "w", encoding="utf-8") as f:
96 f.write(file_1)
95 f.write(file_1)
97
96
98 with prepended_to_syspath(td):
97 with prepended_to_syspath(td):
99 ip.run_cell("import foo")
98 ip.run_cell("import foo")
100
99
101 with tt.AssertPrints("ZeroDivisionError"):
100 with tt.AssertPrints("ZeroDivisionError"):
102 ip.run_cell("foo.f()")
101 ip.run_cell("foo.f()")
103
102
104 def test_iso8859_5(self):
103 def test_iso8859_5(self):
105 with TemporaryDirectory() as td:
104 with TemporaryDirectory() as td:
106 fname = os.path.join(td, 'dfghjkl.py')
105 fname = os.path.join(td, 'dfghjkl.py')
107
106
108 with io.open(fname, 'w', encoding='iso-8859-5') as f:
107 with io.open(fname, 'w', encoding='iso-8859-5') as f:
109 f.write(iso_8859_5_file)
108 f.write(iso_8859_5_file)
110
109
111 with prepended_to_syspath(td):
110 with prepended_to_syspath(td):
112 ip.run_cell("from dfghjkl import fail")
111 ip.run_cell("from dfghjkl import fail")
113
112
114 with tt.AssertPrints("ZeroDivisionError"):
113 with tt.AssertPrints("ZeroDivisionError"):
115 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
114 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
116 ip.run_cell('fail()')
115 ip.run_cell('fail()')
117
116
118 def test_nonascii_msg(self):
117 def test_nonascii_msg(self):
119 cell = u"raise Exception('Γ©')"
118 cell = u"raise Exception('Γ©')"
120 expected = u"Exception('Γ©')"
119 expected = u"Exception('Γ©')"
121 ip.run_cell("%xmode plain")
120 ip.run_cell("%xmode plain")
122 with tt.AssertPrints(expected):
121 with tt.AssertPrints(expected):
123 ip.run_cell(cell)
122 ip.run_cell(cell)
124
123
125 ip.run_cell("%xmode verbose")
124 ip.run_cell("%xmode verbose")
126 with tt.AssertPrints(expected):
125 with tt.AssertPrints(expected):
127 ip.run_cell(cell)
126 ip.run_cell(cell)
128
127
129 ip.run_cell("%xmode context")
128 ip.run_cell("%xmode context")
130 with tt.AssertPrints(expected):
129 with tt.AssertPrints(expected):
131 ip.run_cell(cell)
130 ip.run_cell(cell)
132
131
133 ip.run_cell("%xmode minimal")
132 ip.run_cell("%xmode minimal")
134 with tt.AssertPrints(u"Exception: Γ©"):
133 with tt.AssertPrints(u"Exception: Γ©"):
135 ip.run_cell(cell)
134 ip.run_cell(cell)
136
135
137 # Put this back into Context mode for later tests.
136 # Put this back into Context mode for later tests.
138 ip.run_cell("%xmode context")
137 ip.run_cell("%xmode context")
139
138
140 class NestedGenExprTestCase(unittest.TestCase):
139 class NestedGenExprTestCase(unittest.TestCase):
141 """
140 """
142 Regression test for the following issues:
141 Regression test for the following issues:
143 https://github.com/ipython/ipython/issues/8293
142 https://github.com/ipython/ipython/issues/8293
144 https://github.com/ipython/ipython/issues/8205
143 https://github.com/ipython/ipython/issues/8205
145 """
144 """
146 def test_nested_genexpr(self):
145 def test_nested_genexpr(self):
147 code = dedent(
146 code = dedent(
148 """\
147 """\
149 class SpecificException(Exception):
148 class SpecificException(Exception):
150 pass
149 pass
151
150
152 def foo(x):
151 def foo(x):
153 raise SpecificException("Success!")
152 raise SpecificException("Success!")
154
153
155 sum(sum(foo(x) for _ in [0]) for x in [0])
154 sum(sum(foo(x) for _ in [0]) for x in [0])
156 """
155 """
157 )
156 )
158 with tt.AssertPrints('SpecificException: Success!', suppress=False):
157 with tt.AssertPrints('SpecificException: Success!', suppress=False):
159 ip.run_cell(code)
158 ip.run_cell(code)
160
159
161
160
162 indentationerror_file = """if True:
161 indentationerror_file = """if True:
163 zoon()
162 zoon()
164 """
163 """
165
164
166 class IndentationErrorTest(unittest.TestCase):
165 class IndentationErrorTest(unittest.TestCase):
167 def test_indentationerror_shows_line(self):
166 def test_indentationerror_shows_line(self):
168 # See issue gh-2398
167 # See issue gh-2398
169 with tt.AssertPrints("IndentationError"):
168 with tt.AssertPrints("IndentationError"):
170 with tt.AssertPrints("zoon()", suppress=False):
169 with tt.AssertPrints("zoon()", suppress=False):
171 ip.run_cell(indentationerror_file)
170 ip.run_cell(indentationerror_file)
172
171
173 with TemporaryDirectory() as td:
172 with TemporaryDirectory() as td:
174 fname = os.path.join(td, "foo.py")
173 fname = os.path.join(td, "foo.py")
175 with open(fname, "w", encoding="utf-8") as f:
174 with open(fname, "w", encoding="utf-8") as f:
176 f.write(indentationerror_file)
175 f.write(indentationerror_file)
177
176
178 with tt.AssertPrints("IndentationError"):
177 with tt.AssertPrints("IndentationError"):
179 with tt.AssertPrints("zoon()", suppress=False):
178 with tt.AssertPrints("zoon()", suppress=False):
180 ip.magic('run %s' % fname)
179 ip.magic('run %s' % fname)
181
180
182 se_file_1 = """1
181 se_file_1 = """1
183 2
182 2
184 7/
183 7/
185 """
184 """
186
185
187 se_file_2 = """7/
186 se_file_2 = """7/
188 """
187 """
189
188
190 class SyntaxErrorTest(unittest.TestCase):
189 class SyntaxErrorTest(unittest.TestCase):
191
190
192 def test_syntaxerror_no_stacktrace_at_compile_time(self):
191 def test_syntaxerror_no_stacktrace_at_compile_time(self):
193 syntax_error_at_compile_time = """
192 syntax_error_at_compile_time = """
194 def foo():
193 def foo():
195 ..
194 ..
196 """
195 """
197 with tt.AssertPrints("SyntaxError"):
196 with tt.AssertPrints("SyntaxError"):
198 ip.run_cell(syntax_error_at_compile_time)
197 ip.run_cell(syntax_error_at_compile_time)
199
198
200 with tt.AssertNotPrints("foo()"):
199 with tt.AssertNotPrints("foo()"):
201 ip.run_cell(syntax_error_at_compile_time)
200 ip.run_cell(syntax_error_at_compile_time)
202
201
203 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
202 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
204 syntax_error_at_runtime = """
203 syntax_error_at_runtime = """
205 def foo():
204 def foo():
206 eval("..")
205 eval("..")
207
206
208 def bar():
207 def bar():
209 foo()
208 foo()
210
209
211 bar()
210 bar()
212 """
211 """
213 with tt.AssertPrints("SyntaxError"):
212 with tt.AssertPrints("SyntaxError"):
214 ip.run_cell(syntax_error_at_runtime)
213 ip.run_cell(syntax_error_at_runtime)
215 # Assert syntax error during runtime generate stacktrace
214 # Assert syntax error during runtime generate stacktrace
216 with tt.AssertPrints(["foo()", "bar()"]):
215 with tt.AssertPrints(["foo()", "bar()"]):
217 ip.run_cell(syntax_error_at_runtime)
216 ip.run_cell(syntax_error_at_runtime)
218 del ip.user_ns['bar']
217 del ip.user_ns['bar']
219 del ip.user_ns['foo']
218 del ip.user_ns['foo']
220
219
221 def test_changing_py_file(self):
220 def test_changing_py_file(self):
222 with TemporaryDirectory() as td:
221 with TemporaryDirectory() as td:
223 fname = os.path.join(td, "foo.py")
222 fname = os.path.join(td, "foo.py")
224 with open(fname, "w", encoding="utf-8") as f:
223 with open(fname, "w", encoding="utf-8") as f:
225 f.write(se_file_1)
224 f.write(se_file_1)
226
225
227 with tt.AssertPrints(["7/", "SyntaxError"]):
226 with tt.AssertPrints(["7/", "SyntaxError"]):
228 ip.magic("run " + fname)
227 ip.magic("run " + fname)
229
228
230 # Modify the file
229 # Modify the file
231 with open(fname, "w", encoding="utf-8") as f:
230 with open(fname, "w", encoding="utf-8") as f:
232 f.write(se_file_2)
231 f.write(se_file_2)
233
232
234 # The SyntaxError should point to the correct line
233 # The SyntaxError should point to the correct line
235 with tt.AssertPrints(["7/", "SyntaxError"]):
234 with tt.AssertPrints(["7/", "SyntaxError"]):
236 ip.magic("run " + fname)
235 ip.magic("run " + fname)
237
236
238 def test_non_syntaxerror(self):
237 def test_non_syntaxerror(self):
239 # SyntaxTB may be called with an error other than a SyntaxError
238 # SyntaxTB may be called with an error other than a SyntaxError
240 # See e.g. gh-4361
239 # See e.g. gh-4361
241 try:
240 try:
242 raise ValueError('QWERTY')
241 raise ValueError('QWERTY')
243 except ValueError:
242 except ValueError:
244 with tt.AssertPrints('QWERTY'):
243 with tt.AssertPrints('QWERTY'):
245 ip.showsyntaxerror()
244 ip.showsyntaxerror()
246
245
247 import sys
246 import sys
248
247
249 if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy":
248 if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy":
250 """
249 """
251 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
250 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
252 """
251 """
253 class MemoryErrorTest(unittest.TestCase):
252 class MemoryErrorTest(unittest.TestCase):
254 def test_memoryerror(self):
253 def test_memoryerror(self):
255 memoryerror_code = "(" * 200 + ")" * 200
254 memoryerror_code = "(" * 200 + ")" * 200
256 with tt.AssertPrints("MemoryError"):
255 with tt.AssertPrints("MemoryError"):
257 ip.run_cell(memoryerror_code)
256 ip.run_cell(memoryerror_code)
258
257
259
258
260 class Python3ChainedExceptionsTest(unittest.TestCase):
259 class Python3ChainedExceptionsTest(unittest.TestCase):
261 DIRECT_CAUSE_ERROR_CODE = """
260 DIRECT_CAUSE_ERROR_CODE = """
262 try:
261 try:
263 x = 1 + 2
262 x = 1 + 2
264 print(not_defined_here)
263 print(not_defined_here)
265 except Exception as e:
264 except Exception as e:
266 x += 55
265 x += 55
267 x - 1
266 x - 1
268 y = {}
267 y = {}
269 raise KeyError('uh') from e
268 raise KeyError('uh') from e
270 """
269 """
271
270
272 EXCEPTION_DURING_HANDLING_CODE = """
271 EXCEPTION_DURING_HANDLING_CODE = """
273 try:
272 try:
274 x = 1 + 2
273 x = 1 + 2
275 print(not_defined_here)
274 print(not_defined_here)
276 except Exception as e:
275 except Exception as e:
277 x += 55
276 x += 55
278 x - 1
277 x - 1
279 y = {}
278 y = {}
280 raise KeyError('uh')
279 raise KeyError('uh')
281 """
280 """
282
281
283 SUPPRESS_CHAINING_CODE = """
282 SUPPRESS_CHAINING_CODE = """
284 try:
283 try:
285 1/0
284 1/0
286 except Exception:
285 except Exception:
287 raise ValueError("Yikes") from None
286 raise ValueError("Yikes") from None
288 """
287 """
289
288
290 def test_direct_cause_error(self):
289 def test_direct_cause_error(self):
291 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
290 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
292 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
291 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
293
292
294 def test_exception_during_handling_error(self):
293 def test_exception_during_handling_error(self):
295 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
294 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
296 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
295 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
297
296
298 def test_suppress_exception_chaining(self):
297 def test_suppress_exception_chaining(self):
299 with tt.AssertNotPrints("ZeroDivisionError"), \
298 with tt.AssertNotPrints("ZeroDivisionError"), \
300 tt.AssertPrints("ValueError", suppress=False):
299 tt.AssertPrints("ValueError", suppress=False):
301 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
300 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
302
301
303 def test_plain_direct_cause_error(self):
302 def test_plain_direct_cause_error(self):
304 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
303 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
305 ip.run_cell("%xmode Plain")
304 ip.run_cell("%xmode Plain")
306 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
305 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
307 ip.run_cell("%xmode Verbose")
306 ip.run_cell("%xmode Verbose")
308
307
309 def test_plain_exception_during_handling_error(self):
308 def test_plain_exception_during_handling_error(self):
310 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
309 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
311 ip.run_cell("%xmode Plain")
310 ip.run_cell("%xmode Plain")
312 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
311 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
313 ip.run_cell("%xmode Verbose")
312 ip.run_cell("%xmode Verbose")
314
313
315 def test_plain_suppress_exception_chaining(self):
314 def test_plain_suppress_exception_chaining(self):
316 with tt.AssertNotPrints("ZeroDivisionError"), \
315 with tt.AssertNotPrints("ZeroDivisionError"), \
317 tt.AssertPrints("ValueError", suppress=False):
316 tt.AssertPrints("ValueError", suppress=False):
318 ip.run_cell("%xmode Plain")
317 ip.run_cell("%xmode Plain")
319 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
318 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
320 ip.run_cell("%xmode Verbose")
319 ip.run_cell("%xmode Verbose")
321
320
322
321
323 class RecursionTest(unittest.TestCase):
322 class RecursionTest(unittest.TestCase):
324 DEFINITIONS = """
323 DEFINITIONS = """
325 def non_recurs():
324 def non_recurs():
326 1/0
325 1/0
327
326
328 def r1():
327 def r1():
329 r1()
328 r1()
330
329
331 def r3a():
330 def r3a():
332 r3b()
331 r3b()
333
332
334 def r3b():
333 def r3b():
335 r3c()
334 r3c()
336
335
337 def r3c():
336 def r3c():
338 r3a()
337 r3a()
339
338
340 def r3o1():
339 def r3o1():
341 r3a()
340 r3a()
342
341
343 def r3o2():
342 def r3o2():
344 r3o1()
343 r3o1()
345 """
344 """
346 def setUp(self):
345 def setUp(self):
347 ip.run_cell(self.DEFINITIONS)
346 ip.run_cell(self.DEFINITIONS)
348
347
349 def test_no_recursion(self):
348 def test_no_recursion(self):
350 with tt.AssertNotPrints("skipping similar frames"):
349 with tt.AssertNotPrints("skipping similar frames"):
351 ip.run_cell("non_recurs()")
350 ip.run_cell("non_recurs()")
352
351
353 @recursionlimit(200)
352 @recursionlimit(200)
354 def test_recursion_one_frame(self):
353 def test_recursion_one_frame(self):
355 with tt.AssertPrints(re.compile(
354 with tt.AssertPrints(re.compile(
356 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
355 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
357 ):
356 ):
358 ip.run_cell("r1()")
357 ip.run_cell("r1()")
359
358
360 @recursionlimit(160)
359 @recursionlimit(160)
361 def test_recursion_three_frames(self):
360 def test_recursion_three_frames(self):
362 with tt.AssertPrints("[... skipping similar frames: "), \
361 with tt.AssertPrints("[... skipping similar frames: "), \
363 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
362 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
364 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
363 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
365 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
364 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
366 ip.run_cell("r3o2()")
365 ip.run_cell("r3o2()")
367
366
368
367
369 #----------------------------------------------------------------------------
368 #----------------------------------------------------------------------------
370
369
371 # module testing (minimal)
370 # module testing (minimal)
372 def test_handlers():
371 def test_handlers():
373 def spam(c, d_e):
372 def spam(c, d_e):
374 (d, e) = d_e
373 (d, e) = d_e
375 x = c + d
374 x = c + d
376 y = c * d
375 y = c * d
377 foo(x, y)
376 foo(x, y)
378
377
379 def foo(a, b, bar=1):
378 def foo(a, b, bar=1):
380 eggs(a, b + bar)
379 eggs(a, b + bar)
381
380
382 def eggs(f, g, z=globals()):
381 def eggs(f, g, z=globals()):
383 h = f + g
382 h = f + g
384 i = f - g
383 i = f - g
385 return h / i
384 return h / i
386
385
387 buff = io.StringIO()
386 buff = io.StringIO()
388
387
389 buff.write('')
388 buff.write('')
390 buff.write('*** Before ***')
389 buff.write('*** Before ***')
391 try:
390 try:
392 buff.write(spam(1, (2, 3)))
391 buff.write(spam(1, (2, 3)))
393 except:
392 except:
394 traceback.print_exc(file=buff)
393 traceback.print_exc(file=buff)
395
394
396 handler = ColorTB(ostream=buff)
395 handler = ColorTB(ostream=buff)
397 buff.write('*** ColorTB ***')
396 buff.write('*** ColorTB ***')
398 try:
397 try:
399 buff.write(spam(1, (2, 3)))
398 buff.write(spam(1, (2, 3)))
400 except:
399 except:
401 handler(*sys.exc_info())
400 handler(*sys.exc_info())
402 buff.write('')
401 buff.write('')
403
402
404 handler = VerboseTB(ostream=buff)
403 handler = VerboseTB(ostream=buff)
405 buff.write('*** VerboseTB ***')
404 buff.write('*** VerboseTB ***')
406 try:
405 try:
407 buff.write(spam(1, (2, 3)))
406 buff.write(spam(1, (2, 3)))
408 except:
407 except:
409 handler(*sys.exc_info())
408 handler(*sys.exc_info())
410 buff.write('')
409 buff.write('')
@@ -1,56 +1,57 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test suite for the deepreload module."""
2 """Test suite for the deepreload module."""
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 pytest
8 import types
7 import types
9
10 from pathlib import Path
8 from pathlib import Path
11
9
10 import pytest
11 from tempfile import TemporaryDirectory
12
13 from IPython.lib.deepreload import modules_reloading
14 from IPython.lib.deepreload import reload as dreload
12 from IPython.utils.syspathcontext import prepended_to_syspath
15 from IPython.utils.syspathcontext import prepended_to_syspath
13 from IPython.utils.tempdir import TemporaryDirectory
14 from IPython.lib.deepreload import reload as dreload, modules_reloading
15
16
16
17
17 def test_deepreload():
18 def test_deepreload():
18 "Test that dreload does deep reloads and skips excluded modules."
19 "Test that dreload does deep reloads and skips excluded modules."
19 with TemporaryDirectory() as tmpdir:
20 with TemporaryDirectory() as tmpdir:
20 with prepended_to_syspath(tmpdir):
21 with prepended_to_syspath(tmpdir):
21 tmpdirpath = Path(tmpdir)
22 tmpdirpath = Path(tmpdir)
22 with open(tmpdirpath / "A.py", "w", encoding="utf-8") as f:
23 with open(tmpdirpath / "A.py", "w", encoding="utf-8") as f:
23 f.write("class Object:\n pass\nok = True\n")
24 f.write("class Object:\n pass\nok = True\n")
24 with open(tmpdirpath / "B.py", "w", encoding="utf-8") as f:
25 with open(tmpdirpath / "B.py", "w", encoding="utf-8") as f:
25 f.write("import A\nassert A.ok, 'we are fine'\n")
26 f.write("import A\nassert A.ok, 'we are fine'\n")
26 import A
27 import A
27 import B
28 import B
28
29
29 # Test that A is not reloaded.
30 # Test that A is not reloaded.
30 obj = A.Object()
31 obj = A.Object()
31 dreload(B, exclude=["A"])
32 dreload(B, exclude=["A"])
32 assert isinstance(obj, A.Object) is True
33 assert isinstance(obj, A.Object) is True
33
34
34 # Test that an import failure will not blow-up us.
35 # Test that an import failure will not blow-up us.
35 A.ok = False
36 A.ok = False
36 with pytest.raises(AssertionError, match="we are fine"):
37 with pytest.raises(AssertionError, match="we are fine"):
37 dreload(B, exclude=["A"])
38 dreload(B, exclude=["A"])
38 assert len(modules_reloading) == 0
39 assert len(modules_reloading) == 0
39 assert not A.ok
40 assert not A.ok
40
41
41 # Test that A is reloaded.
42 # Test that A is reloaded.
42 obj = A.Object()
43 obj = A.Object()
43 A.ok = False
44 A.ok = False
44 dreload(B)
45 dreload(B)
45 assert A.ok
46 assert A.ok
46 assert isinstance(obj, A.Object) is False
47 assert isinstance(obj, A.Object) is False
47
48
48
49
49 def test_not_module():
50 def test_not_module():
50 pytest.raises(TypeError, dreload, "modulename")
51 pytest.raises(TypeError, dreload, "modulename")
51
52
52
53
53 def test_not_in_sys_modules():
54 def test_not_in_sys_modules():
54 fake_module = types.ModuleType("fake_module")
55 fake_module = types.ModuleType("fake_module")
55 with pytest.raises(ImportError, match="not in sys.modules"):
56 with pytest.raises(ImportError, match="not in sys.modules"):
56 dreload(fake_module)
57 dreload(fake_module)
@@ -1,59 +1,58 b''
1 """ This module contains classes - NamedFileInTemporaryDirectory, TemporaryWorkingDirectory.
1 """ This module contains classes - NamedFileInTemporaryDirectory, TemporaryWorkingDirectory.
2
2
3 These classes add extra features such as creating a named file in temporary directory and
3 These classes add extra features such as creating a named file in temporary directory and
4 creating a context manager for the working directory which is also temporary.
4 creating a context manager for the working directory which is also temporary.
5 """
5 """
6
6
7 import os as _os
7 import os as _os
8 from pathlib import Path
8 from pathlib import Path
9 from tempfile import TemporaryDirectory
9 from tempfile import TemporaryDirectory
10
10
11
11
12 class NamedFileInTemporaryDirectory(object):
12 class NamedFileInTemporaryDirectory(object):
13
13 def __init__(self, filename, mode="w+b", bufsize=-1, add_to_syspath=False, **kwds):
14 def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
15 """
14 """
16 Open a file named `filename` in a temporary directory.
15 Open a file named `filename` in a temporary directory.
17
16
18 This context manager is preferred over `NamedTemporaryFile` in
17 This context manager is preferred over `NamedTemporaryFile` in
19 stdlib `tempfile` when one needs to reopen the file.
18 stdlib `tempfile` when one needs to reopen the file.
20
19
21 Arguments `mode` and `bufsize` are passed to `open`.
20 Arguments `mode` and `bufsize` are passed to `open`.
22 Rest of the arguments are passed to `TemporaryDirectory`.
21 Rest of the arguments are passed to `TemporaryDirectory`.
23
22
24 """
23 """
25 self._tmpdir = TemporaryDirectory(**kwds)
24 self._tmpdir = TemporaryDirectory(**kwds)
26 path = Path(self._tmpdir.name) / filename
25 path = Path(self._tmpdir.name) / filename
27 encoding = None if "b" in mode else "utf-8"
26 encoding = None if "b" in mode else "utf-8"
28 self.file = open(path, mode, bufsize, encoding=encoding)
27 self.file = open(path, mode, bufsize, encoding=encoding)
29
28
30 def cleanup(self):
29 def cleanup(self):
31 self.file.close()
30 self.file.close()
32 self._tmpdir.cleanup()
31 self._tmpdir.cleanup()
33
32
34 __del__ = cleanup
33 __del__ = cleanup
35
34
36 def __enter__(self):
35 def __enter__(self):
37 return self.file
36 return self.file
38
37
39 def __exit__(self, type, value, traceback):
38 def __exit__(self, type, value, traceback):
40 self.cleanup()
39 self.cleanup()
41
40
42
41
43 class TemporaryWorkingDirectory(TemporaryDirectory):
42 class TemporaryWorkingDirectory(TemporaryDirectory):
44 """
43 """
45 Creates a temporary directory and sets the cwd to that directory.
44 Creates a temporary directory and sets the cwd to that directory.
46 Automatically reverts to previous cwd upon cleanup.
45 Automatically reverts to previous cwd upon cleanup.
47 Usage example:
46 Usage example:
48
47
49 with TemporaryWorkingDirectory() as tmpdir:
48 with TemporaryWorkingDirectory() as tmpdir:
50 ...
49 ...
51 """
50 """
52 def __enter__(self):
51 def __enter__(self):
53 self.old_wd = Path.cwd()
52 self.old_wd = Path.cwd()
54 _os.chdir(self.name)
53 _os.chdir(self.name)
55 return super(TemporaryWorkingDirectory, self).__enter__()
54 return super(TemporaryWorkingDirectory, self).__enter__()
56
55
57 def __exit__(self, exc, value, tb):
56 def __exit__(self, exc, value, tb):
58 _os.chdir(self.old_wd)
57 _os.chdir(self.old_wd)
59 return super(TemporaryWorkingDirectory, self).__exit__(exc, value, tb)
58 return super(TemporaryWorkingDirectory, self).__exit__(exc, value, tb)
@@ -1,509 +1,509 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import 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 unittest.mock import patch
14 from os.path import join, abspath
15 from importlib import reload
13 from importlib import reload
14 from os.path import abspath, join
15 from unittest.mock import patch
16
16
17 import pytest
17 import pytest
18 from tempfile import TemporaryDirectory
18
19
19 import IPython
20 import IPython
20 from IPython import paths
21 from IPython import paths
21 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
22 from IPython.testing.decorators import (
23 from IPython.testing.decorators import (
24 onlyif_unicode_paths,
23 skip_if_not_win32,
25 skip_if_not_win32,
24 skip_win32,
26 skip_win32,
25 onlyif_unicode_paths,
26 )
27 )
27 from IPython.testing.tools import make_tempfile
28 from IPython.testing.tools import make_tempfile
28 from IPython.utils import path
29 from IPython.utils import path
29 from IPython.utils.tempdir import TemporaryDirectory
30
31
30
32 # Platform-dependent imports
31 # Platform-dependent imports
33 try:
32 try:
34 import winreg as wreg
33 import winreg as wreg
35 except ImportError:
34 except ImportError:
36 #Fake _winreg module on non-windows platforms
35 #Fake _winreg module on non-windows platforms
37 import types
36 import types
38 wr_name = "winreg"
37 wr_name = "winreg"
39 sys.modules[wr_name] = types.ModuleType(wr_name)
38 sys.modules[wr_name] = types.ModuleType(wr_name)
40 try:
39 try:
41 import winreg as wreg
40 import winreg as wreg
42 except ImportError:
41 except ImportError:
43 import _winreg as wreg
42 import _winreg as wreg
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 def setup_environment():
79 """Setup testenvironment for some functions that are tested
79 """Setup testenvironment for some functions that are tested
80 in this module. In particular this functions stores attributes
80 in this module. In particular this functions stores attributes
81 and other things that we need to stub in some test functions.
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
82 This needs to be done on a function level and not module level because
83 each testfunction needs a pristine environment.
83 each testfunction needs a pristine environment.
84 """
84 """
85 global oldstuff, platformstuff
85 global oldstuff, platformstuff
86 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
86 oldstuff = (env.copy(), os.name, sys.platform, path.get_home_dir, IPython.__file__, os.getcwd())
87
87
88 def teardown_environment():
88 def teardown_environment():
89 """Restore things that were remembered by the setup_environment function
89 """Restore things that were remembered by the setup_environment function
90 """
90 """
91 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
91 (oldenv, os.name, sys.platform, path.get_home_dir, IPython.__file__, old_wd) = oldstuff
92 os.chdir(old_wd)
92 os.chdir(old_wd)
93 reload(path)
93 reload(path)
94
94
95 for key in list(env):
95 for key in list(env):
96 if key not in oldenv:
96 if key not in oldenv:
97 del env[key]
97 del env[key]
98 env.update(oldenv)
98 env.update(oldenv)
99 if hasattr(sys, 'frozen'):
99 if hasattr(sys, 'frozen'):
100 del sys.frozen
100 del sys.frozen
101
101
102
102
103 # Build decorator that uses the setup_environment/setup_environment
103 # Build decorator that uses the setup_environment/setup_environment
104 @pytest.fixture
104 @pytest.fixture
105 def environment():
105 def environment():
106 setup_environment()
106 setup_environment()
107 yield
107 yield
108 teardown_environment()
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():
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 sys.frozen = True
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():
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 sys.frozen = True
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(join(HOME_TEST_DIR, "Library.zip/IPython/__init__.py")).lower()
138
138
139 home_dir = path.get_home_dir(True)
139 home_dir = path.get_home_dir(True)
140 assert home_dir == unfrozen
140 assert home_dir == unfrozen
141
141
142
142
143 @skip_win32
143 @skip_win32
144 @with_environment
144 @with_environment
145 def test_get_home_dir_3():
145 def test_get_home_dir_3():
146 """get_home_dir() uses $HOME if set"""
146 """get_home_dir() uses $HOME if set"""
147 env["HOME"] = HOME_TEST_DIR
147 env["HOME"] = HOME_TEST_DIR
148 home_dir = path.get_home_dir(True)
148 home_dir = path.get_home_dir(True)
149 # get_home_dir expands symlinks
149 # get_home_dir expands symlinks
150 assert home_dir == os.path.realpath(env["HOME"])
150 assert home_dir == os.path.realpath(env["HOME"])
151
151
152
152
153 @with_environment
153 @with_environment
154 def test_get_home_dir_4():
154 def test_get_home_dir_4():
155 """get_home_dir() still works if $HOME is not set"""
155 """get_home_dir() still works if $HOME is not set"""
156
156
157 if 'HOME' in env: del env['HOME']
157 if 'HOME' in env: del env['HOME']
158 # this should still succeed, but we don't care what the answer is
158 # this should still succeed, but we don't care what the answer is
159 home = path.get_home_dir(False)
159 home = path.get_home_dir(False)
160
160
161 @skip_win32
161 @skip_win32
162 @with_environment
162 @with_environment
163 def test_get_home_dir_5():
163 def test_get_home_dir_5():
164 """raise HomeDirError if $HOME is specified, but not a writable dir"""
164 """raise HomeDirError if $HOME is specified, but not a writable dir"""
165 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
165 env['HOME'] = abspath(HOME_TEST_DIR+'garbage')
166 # set os.name = posix, to prevent My Documents fallback on Windows
166 # set os.name = posix, to prevent My Documents fallback on Windows
167 os.name = 'posix'
167 os.name = 'posix'
168 pytest.raises(path.HomeDirError, path.get_home_dir, True)
168 pytest.raises(path.HomeDirError, path.get_home_dir, True)
169
169
170 # Should we stub wreg fully so we can run the test on all platforms?
170 # Should we stub wreg fully so we can run the test on all platforms?
171 @skip_if_not_win32
171 @skip_if_not_win32
172 @with_environment
172 @with_environment
173 def test_get_home_dir_8():
173 def test_get_home_dir_8():
174 """Using registry hack for 'My Documents', os=='nt'
174 """Using registry hack for 'My Documents', os=='nt'
175
175
176 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
176 HOMESHARE, HOMEDRIVE, HOMEPATH, USERPROFILE and others are missing.
177 """
177 """
178 os.name = 'nt'
178 os.name = 'nt'
179 # Remove from stub environment all keys that may be set
179 # Remove from stub environment all keys that may be set
180 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
180 for key in ['HOME', 'HOMESHARE', 'HOMEDRIVE', 'HOMEPATH', 'USERPROFILE']:
181 env.pop(key, None)
181 env.pop(key, None)
182
182
183 class key:
183 class key:
184 def __enter__(self):
184 def __enter__(self):
185 pass
185 pass
186 def Close(self):
186 def Close(self):
187 pass
187 pass
188 def __exit__(*args, **kwargs):
188 def __exit__(*args, **kwargs):
189 pass
189 pass
190
190
191 with patch.object(wreg, 'OpenKey', return_value=key()), \
191 with patch.object(wreg, 'OpenKey', return_value=key()), \
192 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
192 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
193 home_dir = path.get_home_dir()
193 home_dir = path.get_home_dir()
194 assert home_dir == abspath(HOME_TEST_DIR)
194 assert home_dir == abspath(HOME_TEST_DIR)
195
195
196 @with_environment
196 @with_environment
197 def test_get_xdg_dir_0():
197 def test_get_xdg_dir_0():
198 """test_get_xdg_dir_0, check xdg_dir"""
198 """test_get_xdg_dir_0, check xdg_dir"""
199 reload(path)
199 reload(path)
200 path._writable_dir = lambda path: True
200 path._writable_dir = lambda path: True
201 path.get_home_dir = lambda : 'somewhere'
201 path.get_home_dir = lambda : 'somewhere'
202 os.name = "posix"
202 os.name = "posix"
203 sys.platform = "linux2"
203 sys.platform = "linux2"
204 env.pop('IPYTHON_DIR', None)
204 env.pop('IPYTHON_DIR', None)
205 env.pop('IPYTHONDIR', None)
205 env.pop('IPYTHONDIR', None)
206 env.pop('XDG_CONFIG_HOME', None)
206 env.pop('XDG_CONFIG_HOME', None)
207
207
208 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
208 assert path.get_xdg_dir() == os.path.join("somewhere", ".config")
209
209
210
210
211 @with_environment
211 @with_environment
212 def test_get_xdg_dir_1():
212 def test_get_xdg_dir_1():
213 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
213 """test_get_xdg_dir_1, check nonexistent xdg_dir"""
214 reload(path)
214 reload(path)
215 path.get_home_dir = lambda : HOME_TEST_DIR
215 path.get_home_dir = lambda : HOME_TEST_DIR
216 os.name = "posix"
216 os.name = "posix"
217 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():
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 reload(path)
227 path.get_home_dir = lambda : HOME_TEST_DIR
227 path.get_home_dir = lambda : HOME_TEST_DIR
228 os.name = "posix"
228 os.name = "posix"
229 sys.platform = "linux2"
229 sys.platform = "linux2"
230 env.pop('IPYTHON_DIR', None)
230 env.pop('IPYTHON_DIR', None)
231 env.pop('IPYTHONDIR', None)
231 env.pop('IPYTHONDIR', None)
232 env.pop('XDG_CONFIG_HOME', None)
232 env.pop('XDG_CONFIG_HOME', None)
233 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):
234 if not os.path.exists(cfgdir):
235 os.makedirs(cfgdir)
235 os.makedirs(cfgdir)
236
236
237 assert path.get_xdg_dir() == cfgdir
237 assert path.get_xdg_dir() == cfgdir
238
238
239 @with_environment
239 @with_environment
240 def test_get_xdg_dir_3():
240 def test_get_xdg_dir_3():
241 """test_get_xdg_dir_3, check xdg_dir not used on non-posix systems"""
241 """test_get_xdg_dir_3, check xdg_dir not used on non-posix systems"""
242 reload(path)
242 reload(path)
243 path.get_home_dir = lambda : HOME_TEST_DIR
243 path.get_home_dir = lambda : HOME_TEST_DIR
244 os.name = "nt"
244 os.name = "nt"
245 sys.platform = "win32"
245 sys.platform = "win32"
246 env.pop('IPYTHON_DIR', None)
246 env.pop('IPYTHON_DIR', None)
247 env.pop('IPYTHONDIR', None)
247 env.pop('IPYTHONDIR', None)
248 env.pop('XDG_CONFIG_HOME', None)
248 env.pop('XDG_CONFIG_HOME', None)
249 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)
250 os.makedirs(cfgdir, exist_ok=True)
251
251
252 assert path.get_xdg_dir() is None
252 assert path.get_xdg_dir() is None
253
253
254 def test_filefind():
254 def test_filefind():
255 """Various tests for filefind"""
255 """Various tests for filefind"""
256 f = tempfile.NamedTemporaryFile()
256 f = tempfile.NamedTemporaryFile()
257 # print 'fname:',f.name
257 # print 'fname:',f.name
258 alt_dirs = paths.get_ipython_dir()
258 alt_dirs = paths.get_ipython_dir()
259 t = path.filefind(f.name, alt_dirs)
259 t = path.filefind(f.name, alt_dirs)
260 # print 'found:',t
260 # print 'found:',t
261
261
262
262
263 @dec.skip_if_not_win32
263 @dec.skip_if_not_win32
264 def test_get_long_path_name_win32():
264 def test_get_long_path_name_win32():
265 with TemporaryDirectory() as tmpdir:
265 with TemporaryDirectory() as tmpdir:
266
266
267 # Make a long path. Expands the path of tmpdir prematurely as it may already have a long
267 # 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
268 # 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')
269 long_path = os.path.join(path.get_long_path_name(tmpdir), 'this is my long path name')
270 os.makedirs(long_path)
270 os.makedirs(long_path)
271
271
272 # Test to see if the short path evaluates correctly.
272 # Test to see if the short path evaluates correctly.
273 short_path = os.path.join(tmpdir, 'THISIS~1')
273 short_path = os.path.join(tmpdir, 'THISIS~1')
274 evaluated_path = path.get_long_path_name(short_path)
274 evaluated_path = path.get_long_path_name(short_path)
275 assert evaluated_path.lower() == long_path.lower()
275 assert evaluated_path.lower() == long_path.lower()
276
276
277
277
278 @dec.skip_win32
278 @dec.skip_win32
279 def test_get_long_path_name():
279 def test_get_long_path_name():
280 p = path.get_long_path_name("/usr/local")
280 p = path.get_long_path_name("/usr/local")
281 assert p == "/usr/local"
281 assert p == "/usr/local"
282
282
283
283
284 class TestRaiseDeprecation(unittest.TestCase):
284 class TestRaiseDeprecation(unittest.TestCase):
285
285
286 @dec.skip_win32 # can't create not-user-writable dir on win
286 @dec.skip_win32 # can't create not-user-writable dir on win
287 @with_environment
287 @with_environment
288 def test_not_writable_ipdir(self):
288 def test_not_writable_ipdir(self):
289 tmpdir = tempfile.mkdtemp()
289 tmpdir = tempfile.mkdtemp()
290 os.name = "posix"
290 os.name = "posix"
291 env.pop('IPYTHON_DIR', None)
291 env.pop('IPYTHON_DIR', None)
292 env.pop('IPYTHONDIR', None)
292 env.pop('IPYTHONDIR', None)
293 env.pop('XDG_CONFIG_HOME', None)
293 env.pop('XDG_CONFIG_HOME', None)
294 env['HOME'] = tmpdir
294 env['HOME'] = tmpdir
295 ipdir = os.path.join(tmpdir, '.ipython')
295 ipdir = os.path.join(tmpdir, '.ipython')
296 os.mkdir(ipdir, 0o555)
296 os.mkdir(ipdir, 0o555)
297 try:
297 try:
298 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
298 open(os.path.join(ipdir, "_foo_"), "w", encoding="utf-8").close()
299 except IOError:
299 except IOError:
300 pass
300 pass
301 else:
301 else:
302 # I can still write to an unwritable dir,
302 # I can still write to an unwritable dir,
303 # assume I'm root and skip the test
303 # assume I'm root and skip the test
304 pytest.skip("I can't create directories that I can't write to")
304 pytest.skip("I can't create directories that I can't write to")
305
305
306 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
306 with self.assertWarnsRegex(UserWarning, 'is not a writable location'):
307 ipdir = paths.get_ipython_dir()
307 ipdir = paths.get_ipython_dir()
308 env.pop('IPYTHON_DIR', None)
308 env.pop('IPYTHON_DIR', None)
309
309
310 @with_environment
310 @with_environment
311 def test_get_py_filename():
311 def test_get_py_filename():
312 os.chdir(TMP_TEST_DIR)
312 os.chdir(TMP_TEST_DIR)
313 with make_tempfile("foo.py"):
313 with make_tempfile("foo.py"):
314 assert path.get_py_filename("foo.py") == "foo.py"
314 assert path.get_py_filename("foo.py") == "foo.py"
315 assert path.get_py_filename("foo") == "foo.py"
315 assert path.get_py_filename("foo") == "foo.py"
316 with make_tempfile("foo"):
316 with make_tempfile("foo"):
317 assert path.get_py_filename("foo") == "foo"
317 assert path.get_py_filename("foo") == "foo"
318 pytest.raises(IOError, path.get_py_filename, "foo.py")
318 pytest.raises(IOError, path.get_py_filename, "foo.py")
319 pytest.raises(IOError, path.get_py_filename, "foo")
319 pytest.raises(IOError, path.get_py_filename, "foo")
320 pytest.raises(IOError, path.get_py_filename, "foo.py")
320 pytest.raises(IOError, path.get_py_filename, "foo.py")
321 true_fn = "foo with spaces.py"
321 true_fn = "foo with spaces.py"
322 with make_tempfile(true_fn):
322 with make_tempfile(true_fn):
323 assert path.get_py_filename("foo with spaces") == true_fn
323 assert path.get_py_filename("foo with spaces") == true_fn
324 assert path.get_py_filename("foo with spaces.py") == true_fn
324 assert path.get_py_filename("foo with spaces.py") == true_fn
325 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
325 pytest.raises(IOError, path.get_py_filename, '"foo with spaces.py"')
326 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
326 pytest.raises(IOError, path.get_py_filename, "'foo with spaces.py'")
327
327
328 @onlyif_unicode_paths
328 @onlyif_unicode_paths
329 def test_unicode_in_filename():
329 def test_unicode_in_filename():
330 """When a file doesn't exist, the exception raised should be safe to call
330 """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.
331 str() on - i.e. in Python 2 it must only have ASCII characters.
332
332
333 https://github.com/ipython/ipython/issues/875
333 https://github.com/ipython/ipython/issues/875
334 """
334 """
335 try:
335 try:
336 # these calls should not throw unicode encode exceptions
336 # these calls should not throw unicode encode exceptions
337 path.get_py_filename('fooéè.py')
337 path.get_py_filename('fooéè.py')
338 except IOError as ex:
338 except IOError as ex:
339 str(ex)
339 str(ex)
340
340
341
341
342 class TestShellGlob(unittest.TestCase):
342 class TestShellGlob(unittest.TestCase):
343
343
344 @classmethod
344 @classmethod
345 def setUpClass(cls):
345 def setUpClass(cls):
346 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
346 cls.filenames_start_with_a = ['a0', 'a1', 'a2']
347 cls.filenames_end_with_b = ['0b', '1b', '2b']
347 cls.filenames_end_with_b = ['0b', '1b', '2b']
348 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
348 cls.filenames = cls.filenames_start_with_a + cls.filenames_end_with_b
349 cls.tempdir = TemporaryDirectory()
349 cls.tempdir = TemporaryDirectory()
350 td = cls.tempdir.name
350 td = cls.tempdir.name
351
351
352 with cls.in_tempdir():
352 with cls.in_tempdir():
353 # Create empty files
353 # Create empty files
354 for fname in cls.filenames:
354 for fname in cls.filenames:
355 open(os.path.join(td, fname), "w", encoding="utf-8").close()
355 open(os.path.join(td, fname), "w", encoding="utf-8").close()
356
356
357 @classmethod
357 @classmethod
358 def tearDownClass(cls):
358 def tearDownClass(cls):
359 cls.tempdir.cleanup()
359 cls.tempdir.cleanup()
360
360
361 @classmethod
361 @classmethod
362 @contextmanager
362 @contextmanager
363 def in_tempdir(cls):
363 def in_tempdir(cls):
364 save = os.getcwd()
364 save = os.getcwd()
365 try:
365 try:
366 os.chdir(cls.tempdir.name)
366 os.chdir(cls.tempdir.name)
367 yield
367 yield
368 finally:
368 finally:
369 os.chdir(save)
369 os.chdir(save)
370
370
371 def check_match(self, patterns, matches):
371 def check_match(self, patterns, matches):
372 with self.in_tempdir():
372 with self.in_tempdir():
373 # glob returns unordered list. that's why sorted is required.
373 # glob returns unordered list. that's why sorted is required.
374 assert sorted(path.shellglob(patterns)) == sorted(matches)
374 assert sorted(path.shellglob(patterns)) == sorted(matches)
375
375
376 def common_cases(self):
376 def common_cases(self):
377 return [
377 return [
378 (['*'], self.filenames),
378 (['*'], self.filenames),
379 (['a*'], self.filenames_start_with_a),
379 (['a*'], self.filenames_start_with_a),
380 (['*c'], ['*c']),
380 (['*c'], ['*c']),
381 (['*', 'a*', '*b', '*c'], self.filenames
381 (['*', 'a*', '*b', '*c'], self.filenames
382 + self.filenames_start_with_a
382 + self.filenames_start_with_a
383 + self.filenames_end_with_b
383 + self.filenames_end_with_b
384 + ['*c']),
384 + ['*c']),
385 (['a[012]'], self.filenames_start_with_a),
385 (['a[012]'], self.filenames_start_with_a),
386 ]
386 ]
387
387
388 @skip_win32
388 @skip_win32
389 def test_match_posix(self):
389 def test_match_posix(self):
390 for (patterns, matches) in self.common_cases() + [
390 for (patterns, matches) in self.common_cases() + [
391 ([r'\*'], ['*']),
391 ([r'\*'], ['*']),
392 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
392 ([r'a\*', 'a*'], ['a*'] + self.filenames_start_with_a),
393 ([r'a\[012]'], ['a[012]']),
393 ([r'a\[012]'], ['a[012]']),
394 ]:
394 ]:
395 self.check_match(patterns, matches)
395 self.check_match(patterns, matches)
396
396
397 @skip_if_not_win32
397 @skip_if_not_win32
398 def test_match_windows(self):
398 def test_match_windows(self):
399 for (patterns, matches) in self.common_cases() + [
399 for (patterns, matches) in self.common_cases() + [
400 # In windows, backslash is interpreted as path
400 # In windows, backslash is interpreted as path
401 # separator. Therefore, you can't escape glob
401 # separator. Therefore, you can't escape glob
402 # using it.
402 # using it.
403 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
403 ([r'a\*', 'a*'], [r'a\*'] + self.filenames_start_with_a),
404 ([r'a\[012]'], [r'a\[012]']),
404 ([r'a\[012]'], [r'a\[012]']),
405 ]:
405 ]:
406 self.check_match(patterns, matches)
406 self.check_match(patterns, matches)
407
407
408
408
409 @pytest.mark.parametrize(
409 @pytest.mark.parametrize(
410 "globstr, unescaped_globstr",
410 "globstr, unescaped_globstr",
411 [
411 [
412 (r"\*\[\!\]\?", "*[!]?"),
412 (r"\*\[\!\]\?", "*[!]?"),
413 (r"\\*", r"\*"),
413 (r"\\*", r"\*"),
414 (r"\\\*", r"\*"),
414 (r"\\\*", r"\*"),
415 (r"\\a", r"\a"),
415 (r"\\a", r"\a"),
416 (r"\a", r"\a"),
416 (r"\a", r"\a"),
417 ],
417 ],
418 )
418 )
419 def test_unescape_glob(globstr, unescaped_globstr):
419 def test_unescape_glob(globstr, unescaped_globstr):
420 assert path.unescape_glob(globstr) == unescaped_globstr
420 assert path.unescape_glob(globstr) == unescaped_globstr
421
421
422
422
423 @onlyif_unicode_paths
423 @onlyif_unicode_paths
424 def test_ensure_dir_exists():
424 def test_ensure_dir_exists():
425 with TemporaryDirectory() as td:
425 with TemporaryDirectory() as td:
426 d = os.path.join(td, 'βˆ‚ir')
426 d = os.path.join(td, 'βˆ‚ir')
427 path.ensure_dir_exists(d) # create it
427 path.ensure_dir_exists(d) # create it
428 assert os.path.isdir(d)
428 assert os.path.isdir(d)
429 path.ensure_dir_exists(d) # no-op
429 path.ensure_dir_exists(d) # no-op
430 f = os.path.join(td, "Ζ’ile")
430 f = os.path.join(td, "Ζ’ile")
431 open(f, "w", encoding="utf-8").close() # touch
431 open(f, "w", encoding="utf-8").close() # touch
432 with pytest.raises(IOError):
432 with pytest.raises(IOError):
433 path.ensure_dir_exists(f)
433 path.ensure_dir_exists(f)
434
434
435 class TestLinkOrCopy(unittest.TestCase):
435 class TestLinkOrCopy(unittest.TestCase):
436 def setUp(self):
436 def setUp(self):
437 self.tempdir = TemporaryDirectory()
437 self.tempdir = TemporaryDirectory()
438 self.src = self.dst("src")
438 self.src = self.dst("src")
439 with open(self.src, "w", encoding="utf-8") as f:
439 with open(self.src, "w", encoding="utf-8") as f:
440 f.write("Hello, world!")
440 f.write("Hello, world!")
441
441
442 def tearDown(self):
442 def tearDown(self):
443 self.tempdir.cleanup()
443 self.tempdir.cleanup()
444
444
445 def dst(self, *args):
445 def dst(self, *args):
446 return os.path.join(self.tempdir.name, *args)
446 return os.path.join(self.tempdir.name, *args)
447
447
448 def assert_inode_not_equal(self, a, b):
448 def assert_inode_not_equal(self, a, b):
449 assert (
449 assert (
450 os.stat(a).st_ino != os.stat(b).st_ino
450 os.stat(a).st_ino != os.stat(b).st_ino
451 ), "%r and %r do reference the same indoes" % (a, b)
451 ), "%r and %r do reference the same indoes" % (a, b)
452
452
453 def assert_inode_equal(self, a, b):
453 def assert_inode_equal(self, a, b):
454 assert (
454 assert (
455 os.stat(a).st_ino == os.stat(b).st_ino
455 os.stat(a).st_ino == os.stat(b).st_ino
456 ), "%r and %r do not reference the same indoes" % (a, b)
456 ), "%r and %r do not reference the same indoes" % (a, b)
457
457
458 def assert_content_equal(self, a, b):
458 def assert_content_equal(self, a, b):
459 with open(a, "rb") as a_f:
459 with open(a, "rb") as a_f:
460 with open(b, "rb") as b_f:
460 with open(b, "rb") as b_f:
461 assert a_f.read() == b_f.read()
461 assert a_f.read() == b_f.read()
462
462
463 @skip_win32
463 @skip_win32
464 def test_link_successful(self):
464 def test_link_successful(self):
465 dst = self.dst("target")
465 dst = self.dst("target")
466 path.link_or_copy(self.src, dst)
466 path.link_or_copy(self.src, dst)
467 self.assert_inode_equal(self.src, dst)
467 self.assert_inode_equal(self.src, dst)
468
468
469 @skip_win32
469 @skip_win32
470 def test_link_into_dir(self):
470 def test_link_into_dir(self):
471 dst = self.dst("some_dir")
471 dst = self.dst("some_dir")
472 os.mkdir(dst)
472 os.mkdir(dst)
473 path.link_or_copy(self.src, dst)
473 path.link_or_copy(self.src, dst)
474 expected_dst = self.dst("some_dir", os.path.basename(self.src))
474 expected_dst = self.dst("some_dir", os.path.basename(self.src))
475 self.assert_inode_equal(self.src, expected_dst)
475 self.assert_inode_equal(self.src, expected_dst)
476
476
477 @skip_win32
477 @skip_win32
478 def test_target_exists(self):
478 def test_target_exists(self):
479 dst = self.dst("target")
479 dst = self.dst("target")
480 open(dst, "w", encoding="utf-8").close()
480 open(dst, "w", encoding="utf-8").close()
481 path.link_or_copy(self.src, dst)
481 path.link_or_copy(self.src, dst)
482 self.assert_inode_equal(self.src, dst)
482 self.assert_inode_equal(self.src, dst)
483
483
484 @skip_win32
484 @skip_win32
485 def test_no_link(self):
485 def test_no_link(self):
486 real_link = os.link
486 real_link = os.link
487 try:
487 try:
488 del os.link
488 del os.link
489 dst = self.dst("target")
489 dst = self.dst("target")
490 path.link_or_copy(self.src, dst)
490 path.link_or_copy(self.src, dst)
491 self.assert_content_equal(self.src, dst)
491 self.assert_content_equal(self.src, dst)
492 self.assert_inode_not_equal(self.src, dst)
492 self.assert_inode_not_equal(self.src, dst)
493 finally:
493 finally:
494 os.link = real_link
494 os.link = real_link
495
495
496 @skip_if_not_win32
496 @skip_if_not_win32
497 def test_windows(self):
497 def test_windows(self):
498 dst = self.dst("target")
498 dst = self.dst("target")
499 path.link_or_copy(self.src, dst)
499 path.link_or_copy(self.src, dst)
500 self.assert_content_equal(self.src, dst)
500 self.assert_content_equal(self.src, dst)
501
501
502 def test_link_twice(self):
502 def test_link_twice(self):
503 # Linking the same file twice shouldn't leave duplicates around.
503 # Linking the same file twice shouldn't leave duplicates around.
504 # See https://github.com/ipython/ipython/issues/6450
504 # See https://github.com/ipython/ipython/issues/6450
505 dst = self.dst('target')
505 dst = self.dst('target')
506 path.link_or_copy(self.src, dst)
506 path.link_or_copy(self.src, dst)
507 path.link_or_copy(self.src, dst)
507 path.link_or_copy(self.src, dst)
508 self.assert_inode_equal(self.src, dst)
508 self.assert_inode_equal(self.src, dst)
509 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
509 assert sorted(os.listdir(self.tempdir.name)) == ["src", "target"]
General Comments 0
You need to be logged in to leave comments. Login now