##// END OF EJS Templates
Move optional dependencies to a separate set
Nikita Kniazev -
Show More
@@ -1,50 +1,55 b''
1 name: Run tests
1 name: Run tests
2
2
3 on:
3 on:
4 push:
4 push:
5 pull_request:
5 pull_request:
6 # Run weekly on Monday at 1:23 UTC
6 # Run weekly on Monday at 1:23 UTC
7 schedule:
7 schedule:
8 - cron: '23 1 * * 1'
8 - cron: '23 1 * * 1'
9 workflow_dispatch:
9 workflow_dispatch:
10
10
11
11
12 jobs:
12 jobs:
13 test:
13 test:
14 runs-on: ${{ matrix.os }}
14 runs-on: ${{ matrix.os }}
15 strategy:
15 strategy:
16 matrix:
16 matrix:
17 os: [ubuntu-latest]
17 os: [ubuntu-latest]
18 python-version: ["3.7", "3.8", "3.9", "3.10"]
18 python-version: ["3.7", "3.8", "3.9", "3.10"]
19 deps: [test_extra]
19 # Test all on ubuntu, test ends on macos
20 # Test all on ubuntu, test ends on macos
20 include:
21 include:
21 - os: macos-latest
22 - os: macos-latest
22 python-version: "3.7"
23 python-version: "3.7"
24 deps: test_extra
23 - os: macos-latest
25 - os: macos-latest
24 python-version: "3.10"
26 python-version: "3.10"
27 deps: test_extra
28 # Tests minimal dependencies set
29 - os: ubuntu-latest
30 python-version: "3.10"
31 deps: test
25
32
26 steps:
33 steps:
27 - uses: actions/checkout@v2
34 - uses: actions/checkout@v2
28 - name: Set up Python ${{ matrix.python-version }}
35 - name: Set up Python ${{ matrix.python-version }}
29 uses: actions/setup-python@v2
36 uses: actions/setup-python@v2
30 with:
37 with:
31 python-version: ${{ matrix.python-version }}
38 python-version: ${{ matrix.python-version }}
32 - name: Install latex
39 - name: Install latex
33 if: runner.os == 'Linux'
40 if: runner.os == 'Linux' && matrix.deps == 'test_extra'
34 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
41 run: sudo apt-get -yq -o Acquire::Retries=3 --no-install-suggests --no-install-recommends install texlive dvipng
35 - name: Install and update Python dependencies
42 - name: Install and update Python dependencies
36 run: |
43 run: |
37 python -m pip install --upgrade pip setuptools wheel
44 python -m pip install --upgrade pip setuptools wheel
38 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
45 python -m pip install --upgrade -e .[${{ matrix.deps }}]
39 python -m pip install --upgrade --upgrade-strategy eager trio curio
46 python -m pip install --upgrade check-manifest pytest-cov
40 python -m pip install --upgrade pytest pytest-cov pytest-trio 'matplotlib!=3.2.0' pandas
41 python -m pip install --upgrade check-manifest pytest-cov anyio
42 - name: Check manifest
47 - name: Check manifest
43 run: check-manifest
48 run: check-manifest
44 - name: pytest
49 - name: pytest
45 env:
50 env:
46 COLUMNS: 120
51 COLUMNS: 120
47 run: |
52 run: |
48 pytest --color=yes -v --cov --cov-report=xml
53 pytest --color=yes -v --cov --cov-report=xml
49 - name: Upload coverage to Codecov
54 - name: Upload coverage to Codecov
50 uses: codecov/codecov-action@v2
55 uses: codecov/codecov-action@v2
@@ -1,1364 +1,1365 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions."""
2 """Tests for various magic functions."""
3
3
4 import asyncio
4 import asyncio
5 import io
5 import io
6 import os
6 import os
7 import re
7 import re
8 import shlex
8 import shlex
9 import sys
9 import sys
10 import warnings
10 import warnings
11 from importlib import invalidate_caches
11 from importlib import invalidate_caches
12 from io import StringIO
12 from io import StringIO
13 from pathlib import Path
13 from pathlib import Path
14 from textwrap import dedent
14 from textwrap import dedent
15 from unittest import TestCase, mock
15 from unittest import TestCase, mock
16
16
17 import pytest
17 import pytest
18
18
19 from IPython import get_ipython
19 from IPython import get_ipython
20 from IPython.core import magic
20 from IPython.core import magic
21 from IPython.core.error import UsageError
21 from IPython.core.error import UsageError
22 from IPython.core.magic import (
22 from IPython.core.magic import (
23 Magics,
23 Magics,
24 cell_magic,
24 cell_magic,
25 line_magic,
25 line_magic,
26 magics_class,
26 magics_class,
27 register_cell_magic,
27 register_cell_magic,
28 register_line_magic,
28 register_line_magic,
29 )
29 )
30 from IPython.core.magics import code, execution, logging, osm, script
30 from IPython.core.magics import code, execution, logging, osm, script
31 from IPython.testing import decorators as dec
31 from IPython.testing import decorators as dec
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils.io import capture_output
33 from IPython.utils.io import capture_output
34 from IPython.utils.process import find_cmd
34 from IPython.utils.process import find_cmd
35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36
36
37 from .test_debugger import PdbTestInput
37 from .test_debugger import PdbTestInput
38
38
39
39
40 @magic.magics_class
40 @magic.magics_class
41 class DummyMagics(magic.Magics): pass
41 class DummyMagics(magic.Magics): pass
42
42
43 def test_extract_code_ranges():
43 def test_extract_code_ranges():
44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
45 expected = [
45 expected = [
46 (0, 1),
46 (0, 1),
47 (2, 3),
47 (2, 3),
48 (4, 6),
48 (4, 6),
49 (6, 9),
49 (6, 9),
50 (9, 14),
50 (9, 14),
51 (16, None),
51 (16, None),
52 (None, 9),
52 (None, 9),
53 (9, None),
53 (9, None),
54 (None, 13),
54 (None, 13),
55 (None, None),
55 (None, None),
56 ]
56 ]
57 actual = list(code.extract_code_ranges(instr))
57 actual = list(code.extract_code_ranges(instr))
58 assert actual == expected
58 assert actual == expected
59
59
60 def test_extract_symbols():
60 def test_extract_symbols():
61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
63 expected = [([], ['a']),
63 expected = [([], ['a']),
64 (["def b():\n return 42\n"], []),
64 (["def b():\n return 42\n"], []),
65 (["class A: pass\n"], []),
65 (["class A: pass\n"], []),
66 (["class A: pass\n", "def b():\n return 42\n"], []),
66 (["class A: pass\n", "def b():\n return 42\n"], []),
67 (["class A: pass\n"], ['a']),
67 (["class A: pass\n"], ['a']),
68 ([], ['z'])]
68 ([], ['z'])]
69 for symbols, exp in zip(symbols_args, expected):
69 for symbols, exp in zip(symbols_args, expected):
70 assert code.extract_symbols(source, symbols) == exp
70 assert code.extract_symbols(source, symbols) == exp
71
71
72
72
73 def test_extract_symbols_raises_exception_with_non_python_code():
73 def test_extract_symbols_raises_exception_with_non_python_code():
74 source = ("=begin A Ruby program :)=end\n"
74 source = ("=begin A Ruby program :)=end\n"
75 "def hello\n"
75 "def hello\n"
76 "puts 'Hello world'\n"
76 "puts 'Hello world'\n"
77 "end")
77 "end")
78 with pytest.raises(SyntaxError):
78 with pytest.raises(SyntaxError):
79 code.extract_symbols(source, "hello")
79 code.extract_symbols(source, "hello")
80
80
81
81
82 def test_magic_not_found():
82 def test_magic_not_found():
83 # magic not found raises UsageError
83 # magic not found raises UsageError
84 with pytest.raises(UsageError):
84 with pytest.raises(UsageError):
85 _ip.magic('doesntexist')
85 _ip.magic('doesntexist')
86
86
87 # ensure result isn't success when a magic isn't found
87 # ensure result isn't success when a magic isn't found
88 result = _ip.run_cell('%doesntexist')
88 result = _ip.run_cell('%doesntexist')
89 assert isinstance(result.error_in_exec, UsageError)
89 assert isinstance(result.error_in_exec, UsageError)
90
90
91
91
92 def test_cell_magic_not_found():
92 def test_cell_magic_not_found():
93 # magic not found raises UsageError
93 # magic not found raises UsageError
94 with pytest.raises(UsageError):
94 with pytest.raises(UsageError):
95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
96
96
97 # ensure result isn't success when a magic isn't found
97 # ensure result isn't success when a magic isn't found
98 result = _ip.run_cell('%%doesntexist')
98 result = _ip.run_cell('%%doesntexist')
99 assert isinstance(result.error_in_exec, UsageError)
99 assert isinstance(result.error_in_exec, UsageError)
100
100
101
101
102 def test_magic_error_status():
102 def test_magic_error_status():
103 def fail(shell):
103 def fail(shell):
104 1/0
104 1/0
105 _ip.register_magic_function(fail)
105 _ip.register_magic_function(fail)
106 result = _ip.run_cell('%fail')
106 result = _ip.run_cell('%fail')
107 assert isinstance(result.error_in_exec, ZeroDivisionError)
107 assert isinstance(result.error_in_exec, ZeroDivisionError)
108
108
109
109
110 def test_config():
110 def test_config():
111 """ test that config magic does not raise
111 """ test that config magic does not raise
112 can happen if Configurable init is moved too early into
112 can happen if Configurable init is moved too early into
113 Magics.__init__ as then a Config object will be registered as a
113 Magics.__init__ as then a Config object will be registered as a
114 magic.
114 magic.
115 """
115 """
116 ## should not raise.
116 ## should not raise.
117 _ip.magic('config')
117 _ip.magic('config')
118
118
119 def test_config_available_configs():
119 def test_config_available_configs():
120 """ test that config magic prints available configs in unique and
120 """ test that config magic prints available configs in unique and
121 sorted order. """
121 sorted order. """
122 with capture_output() as captured:
122 with capture_output() as captured:
123 _ip.magic('config')
123 _ip.magic('config')
124
124
125 stdout = captured.stdout
125 stdout = captured.stdout
126 config_classes = stdout.strip().split('\n')[1:]
126 config_classes = stdout.strip().split('\n')[1:]
127 assert config_classes == sorted(set(config_classes))
127 assert config_classes == sorted(set(config_classes))
128
128
129 def test_config_print_class():
129 def test_config_print_class():
130 """ test that config with a classname prints the class's options. """
130 """ test that config with a classname prints the class's options. """
131 with capture_output() as captured:
131 with capture_output() as captured:
132 _ip.magic('config TerminalInteractiveShell')
132 _ip.magic('config TerminalInteractiveShell')
133
133
134 stdout = captured.stdout
134 stdout = captured.stdout
135 assert re.match(
135 assert re.match(
136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
138
138
139
139
140 def test_rehashx():
140 def test_rehashx():
141 # clear up everything
141 # clear up everything
142 _ip.alias_manager.clear_aliases()
142 _ip.alias_manager.clear_aliases()
143 del _ip.db['syscmdlist']
143 del _ip.db['syscmdlist']
144
144
145 _ip.magic('rehashx')
145 _ip.magic('rehashx')
146 # Practically ALL ipython development systems will have more than 10 aliases
146 # Practically ALL ipython development systems will have more than 10 aliases
147
147
148 assert len(_ip.alias_manager.aliases) > 10
148 assert len(_ip.alias_manager.aliases) > 10
149 for name, cmd in _ip.alias_manager.aliases:
149 for name, cmd in _ip.alias_manager.aliases:
150 # we must strip dots from alias names
150 # we must strip dots from alias names
151 assert "." not in name
151 assert "." not in name
152
152
153 # rehashx must fill up syscmdlist
153 # rehashx must fill up syscmdlist
154 scoms = _ip.db['syscmdlist']
154 scoms = _ip.db['syscmdlist']
155 assert len(scoms) > 10
155 assert len(scoms) > 10
156
156
157
157
158 def test_magic_parse_options():
158 def test_magic_parse_options():
159 """Test that we don't mangle paths when parsing magic options."""
159 """Test that we don't mangle paths when parsing magic options."""
160 ip = get_ipython()
160 ip = get_ipython()
161 path = 'c:\\x'
161 path = 'c:\\x'
162 m = DummyMagics(ip)
162 m = DummyMagics(ip)
163 opts = m.parse_options('-f %s' % path,'f:')[0]
163 opts = m.parse_options('-f %s' % path,'f:')[0]
164 # argv splitting is os-dependent
164 # argv splitting is os-dependent
165 if os.name == 'posix':
165 if os.name == 'posix':
166 expected = 'c:x'
166 expected = 'c:x'
167 else:
167 else:
168 expected = path
168 expected = path
169 assert opts["f"] == expected
169 assert opts["f"] == expected
170
170
171
171
172 def test_magic_parse_long_options():
172 def test_magic_parse_long_options():
173 """Magic.parse_options can handle --foo=bar long options"""
173 """Magic.parse_options can handle --foo=bar long options"""
174 ip = get_ipython()
174 ip = get_ipython()
175 m = DummyMagics(ip)
175 m = DummyMagics(ip)
176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
177 assert "foo" in opts
177 assert "foo" in opts
178 assert "bar" in opts
178 assert "bar" in opts
179 assert opts["bar"] == "bubble"
179 assert opts["bar"] == "bubble"
180
180
181
181
182 def doctest_hist_f():
182 def doctest_hist_f():
183 """Test %hist -f with temporary filename.
183 """Test %hist -f with temporary filename.
184
184
185 In [9]: import tempfile
185 In [9]: import tempfile
186
186
187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
188
188
189 In [11]: %hist -nl -f $tfile 3
189 In [11]: %hist -nl -f $tfile 3
190
190
191 In [13]: import os; os.unlink(tfile)
191 In [13]: import os; os.unlink(tfile)
192 """
192 """
193
193
194
194
195 def doctest_hist_op():
195 def doctest_hist_op():
196 """Test %hist -op
196 """Test %hist -op
197
197
198 In [1]: class b(float):
198 In [1]: class b(float):
199 ...: pass
199 ...: pass
200 ...:
200 ...:
201
201
202 In [2]: class s(object):
202 In [2]: class s(object):
203 ...: def __str__(self):
203 ...: def __str__(self):
204 ...: return 's'
204 ...: return 's'
205 ...:
205 ...:
206
206
207 In [3]:
207 In [3]:
208
208
209 In [4]: class r(b):
209 In [4]: class r(b):
210 ...: def __repr__(self):
210 ...: def __repr__(self):
211 ...: return 'r'
211 ...: return 'r'
212 ...:
212 ...:
213
213
214 In [5]: class sr(s,r): pass
214 In [5]: class sr(s,r): pass
215 ...:
215 ...:
216
216
217 In [6]:
217 In [6]:
218
218
219 In [7]: bb=b()
219 In [7]: bb=b()
220
220
221 In [8]: ss=s()
221 In [8]: ss=s()
222
222
223 In [9]: rr=r()
223 In [9]: rr=r()
224
224
225 In [10]: ssrr=sr()
225 In [10]: ssrr=sr()
226
226
227 In [11]: 4.5
227 In [11]: 4.5
228 Out[11]: 4.5
228 Out[11]: 4.5
229
229
230 In [12]: str(ss)
230 In [12]: str(ss)
231 Out[12]: 's'
231 Out[12]: 's'
232
232
233 In [13]:
233 In [13]:
234
234
235 In [14]: %hist -op
235 In [14]: %hist -op
236 >>> class b:
236 >>> class b:
237 ... pass
237 ... pass
238 ...
238 ...
239 >>> class s(b):
239 >>> class s(b):
240 ... def __str__(self):
240 ... def __str__(self):
241 ... return 's'
241 ... return 's'
242 ...
242 ...
243 >>>
243 >>>
244 >>> class r(b):
244 >>> class r(b):
245 ... def __repr__(self):
245 ... def __repr__(self):
246 ... return 'r'
246 ... return 'r'
247 ...
247 ...
248 >>> class sr(s,r): pass
248 >>> class sr(s,r): pass
249 >>>
249 >>>
250 >>> bb=b()
250 >>> bb=b()
251 >>> ss=s()
251 >>> ss=s()
252 >>> rr=r()
252 >>> rr=r()
253 >>> ssrr=sr()
253 >>> ssrr=sr()
254 >>> 4.5
254 >>> 4.5
255 4.5
255 4.5
256 >>> str(ss)
256 >>> str(ss)
257 's'
257 's'
258 >>>
258 >>>
259 """
259 """
260
260
261 def test_hist_pof():
261 def test_hist_pof():
262 ip = get_ipython()
262 ip = get_ipython()
263 ip.run_cell("1+2", store_history=True)
263 ip.run_cell("1+2", store_history=True)
264 #raise Exception(ip.history_manager.session_number)
264 #raise Exception(ip.history_manager.session_number)
265 #raise Exception(list(ip.history_manager._get_range_session()))
265 #raise Exception(list(ip.history_manager._get_range_session()))
266 with TemporaryDirectory() as td:
266 with TemporaryDirectory() as td:
267 tf = os.path.join(td, 'hist.py')
267 tf = os.path.join(td, 'hist.py')
268 ip.run_line_magic('history', '-pof %s' % tf)
268 ip.run_line_magic('history', '-pof %s' % tf)
269 assert os.path.isfile(tf)
269 assert os.path.isfile(tf)
270
270
271
271
272 def test_macro():
272 def test_macro():
273 ip = get_ipython()
273 ip = get_ipython()
274 ip.history_manager.reset() # Clear any existing history.
274 ip.history_manager.reset() # Clear any existing history.
275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
276 for i, cmd in enumerate(cmds, start=1):
276 for i, cmd in enumerate(cmds, start=1):
277 ip.history_manager.store_inputs(i, cmd)
277 ip.history_manager.store_inputs(i, cmd)
278 ip.magic("macro test 1-3")
278 ip.magic("macro test 1-3")
279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
280
280
281 # List macros
281 # List macros
282 assert "test" in ip.magic("macro")
282 assert "test" in ip.magic("macro")
283
283
284
284
285 def test_macro_run():
285 def test_macro_run():
286 """Test that we can run a multi-line macro successfully."""
286 """Test that we can run a multi-line macro successfully."""
287 ip = get_ipython()
287 ip = get_ipython()
288 ip.history_manager.reset()
288 ip.history_manager.reset()
289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
290 for cmd in cmds:
290 for cmd in cmds:
291 ip.run_cell(cmd, store_history=True)
291 ip.run_cell(cmd, store_history=True)
292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
293 with tt.AssertPrints("12"):
293 with tt.AssertPrints("12"):
294 ip.run_cell("test")
294 ip.run_cell("test")
295 with tt.AssertPrints("13"):
295 with tt.AssertPrints("13"):
296 ip.run_cell("test")
296 ip.run_cell("test")
297
297
298
298
299 def test_magic_magic():
299 def test_magic_magic():
300 """Test %magic"""
300 """Test %magic"""
301 ip = get_ipython()
301 ip = get_ipython()
302 with capture_output() as captured:
302 with capture_output() as captured:
303 ip.magic("magic")
303 ip.magic("magic")
304
304
305 stdout = captured.stdout
305 stdout = captured.stdout
306 assert "%magic" in stdout
306 assert "%magic" in stdout
307 assert "IPython" in stdout
307 assert "IPython" in stdout
308 assert "Available" in stdout
308 assert "Available" in stdout
309
309
310
310
311 @dec.skipif_not_numpy
311 @dec.skipif_not_numpy
312 def test_numpy_reset_array_undec():
312 def test_numpy_reset_array_undec():
313 "Test '%reset array' functionality"
313 "Test '%reset array' functionality"
314 _ip.ex("import numpy as np")
314 _ip.ex("import numpy as np")
315 _ip.ex("a = np.empty(2)")
315 _ip.ex("a = np.empty(2)")
316 assert "a" in _ip.user_ns
316 assert "a" in _ip.user_ns
317 _ip.magic("reset -f array")
317 _ip.magic("reset -f array")
318 assert "a" not in _ip.user_ns
318 assert "a" not in _ip.user_ns
319
319
320
320
321 def test_reset_out():
321 def test_reset_out():
322 "Test '%reset out' magic"
322 "Test '%reset out' magic"
323 _ip.run_cell("parrot = 'dead'", store_history=True)
323 _ip.run_cell("parrot = 'dead'", store_history=True)
324 # test '%reset -f out', make an Out prompt
324 # test '%reset -f out', make an Out prompt
325 _ip.run_cell("parrot", store_history=True)
325 _ip.run_cell("parrot", store_history=True)
326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
327 _ip.magic("reset -f out")
327 _ip.magic("reset -f out")
328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 assert len(_ip.user_ns["Out"]) == 0
329 assert len(_ip.user_ns["Out"]) == 0
330
330
331
331
332 def test_reset_in():
332 def test_reset_in():
333 "Test '%reset in' magic"
333 "Test '%reset in' magic"
334 # test '%reset -f in'
334 # test '%reset -f in'
335 _ip.run_cell("parrot", store_history=True)
335 _ip.run_cell("parrot", store_history=True)
336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
337 _ip.magic("%reset -f in")
337 _ip.magic("%reset -f in")
338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 assert len(set(_ip.user_ns["In"])) == 1
339 assert len(set(_ip.user_ns["In"])) == 1
340
340
341
341
342 def test_reset_dhist():
342 def test_reset_dhist():
343 "Test '%reset dhist' magic"
343 "Test '%reset dhist' magic"
344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
346 _ip.magic("cd -")
346 _ip.magic("cd -")
347 assert len(_ip.user_ns["_dh"]) > 0
347 assert len(_ip.user_ns["_dh"]) > 0
348 _ip.magic("reset -f dhist")
348 _ip.magic("reset -f dhist")
349 assert len(_ip.user_ns["_dh"]) == 0
349 assert len(_ip.user_ns["_dh"]) == 0
350 _ip.run_cell("_dh = [d for d in tmp]") # restore
350 _ip.run_cell("_dh = [d for d in tmp]") # restore
351
351
352
352
353 def test_reset_in_length():
353 def test_reset_in_length():
354 "Test that '%reset in' preserves In[] length"
354 "Test that '%reset in' preserves In[] length"
355 _ip.run_cell("print 'foo'")
355 _ip.run_cell("print 'foo'")
356 _ip.run_cell("reset -f in")
356 _ip.run_cell("reset -f in")
357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
358
358
359
359
360 class TestResetErrors(TestCase):
360 class TestResetErrors(TestCase):
361
361
362 def test_reset_redefine(self):
362 def test_reset_redefine(self):
363
363
364 @magics_class
364 @magics_class
365 class KernelMagics(Magics):
365 class KernelMagics(Magics):
366 @line_magic
366 @line_magic
367 def less(self, shell): pass
367 def less(self, shell): pass
368
368
369 _ip.register_magics(KernelMagics)
369 _ip.register_magics(KernelMagics)
370
370
371 with self.assertLogs() as cm:
371 with self.assertLogs() as cm:
372 # hack, we want to just capture logs, but assertLogs fails if not
372 # hack, we want to just capture logs, but assertLogs fails if not
373 # logs get produce.
373 # logs get produce.
374 # so log one things we ignore.
374 # so log one things we ignore.
375 import logging as log_mod
375 import logging as log_mod
376 log = log_mod.getLogger()
376 log = log_mod.getLogger()
377 log.info('Nothing')
377 log.info('Nothing')
378 # end hack.
378 # end hack.
379 _ip.run_cell("reset -f")
379 _ip.run_cell("reset -f")
380
380
381 assert len(cm.output) == 1
381 assert len(cm.output) == 1
382 for out in cm.output:
382 for out in cm.output:
383 assert "Invalid alias" not in out
383 assert "Invalid alias" not in out
384
384
385 def test_tb_syntaxerror():
385 def test_tb_syntaxerror():
386 """test %tb after a SyntaxError"""
386 """test %tb after a SyntaxError"""
387 ip = get_ipython()
387 ip = get_ipython()
388 ip.run_cell("for")
388 ip.run_cell("for")
389
389
390 # trap and validate stdout
390 # trap and validate stdout
391 save_stdout = sys.stdout
391 save_stdout = sys.stdout
392 try:
392 try:
393 sys.stdout = StringIO()
393 sys.stdout = StringIO()
394 ip.run_cell("%tb")
394 ip.run_cell("%tb")
395 out = sys.stdout.getvalue()
395 out = sys.stdout.getvalue()
396 finally:
396 finally:
397 sys.stdout = save_stdout
397 sys.stdout = save_stdout
398 # trim output, and only check the last line
398 # trim output, and only check the last line
399 last_line = out.rstrip().splitlines()[-1].strip()
399 last_line = out.rstrip().splitlines()[-1].strip()
400 assert last_line == "SyntaxError: invalid syntax"
400 assert last_line == "SyntaxError: invalid syntax"
401
401
402
402
403 def test_time():
403 def test_time():
404 ip = get_ipython()
404 ip = get_ipython()
405
405
406 with tt.AssertPrints("Wall time: "):
406 with tt.AssertPrints("Wall time: "):
407 ip.run_cell("%time None")
407 ip.run_cell("%time None")
408
408
409 ip.run_cell("def f(kmjy):\n"
409 ip.run_cell("def f(kmjy):\n"
410 " %time print (2*kmjy)")
410 " %time print (2*kmjy)")
411
411
412 with tt.AssertPrints("Wall time: "):
412 with tt.AssertPrints("Wall time: "):
413 with tt.AssertPrints("hihi", suppress=False):
413 with tt.AssertPrints("hihi", suppress=False):
414 ip.run_cell("f('hi')")
414 ip.run_cell("f('hi')")
415
415
416 def test_time_last_not_expression():
416 def test_time_last_not_expression():
417 ip.run_cell("%%time\n"
417 ip.run_cell("%%time\n"
418 "var_1 = 1\n"
418 "var_1 = 1\n"
419 "var_2 = 2\n")
419 "var_2 = 2\n")
420 assert ip.user_ns['var_1'] == 1
420 assert ip.user_ns['var_1'] == 1
421 del ip.user_ns['var_1']
421 del ip.user_ns['var_1']
422 assert ip.user_ns['var_2'] == 2
422 assert ip.user_ns['var_2'] == 2
423 del ip.user_ns['var_2']
423 del ip.user_ns['var_2']
424
424
425
425
426 @dec.skip_win32
426 @dec.skip_win32
427 def test_time2():
427 def test_time2():
428 ip = get_ipython()
428 ip = get_ipython()
429
429
430 with tt.AssertPrints("CPU times: user "):
430 with tt.AssertPrints("CPU times: user "):
431 ip.run_cell("%time None")
431 ip.run_cell("%time None")
432
432
433 def test_time3():
433 def test_time3():
434 """Erroneous magic function calls, issue gh-3334"""
434 """Erroneous magic function calls, issue gh-3334"""
435 ip = get_ipython()
435 ip = get_ipython()
436 ip.user_ns.pop('run', None)
436 ip.user_ns.pop('run', None)
437
437
438 with tt.AssertNotPrints("not found", channel='stderr'):
438 with tt.AssertNotPrints("not found", channel='stderr'):
439 ip.run_cell("%%time\n"
439 ip.run_cell("%%time\n"
440 "run = 0\n"
440 "run = 0\n"
441 "run += 1")
441 "run += 1")
442
442
443 def test_multiline_time():
443 def test_multiline_time():
444 """Make sure last statement from time return a value."""
444 """Make sure last statement from time return a value."""
445 ip = get_ipython()
445 ip = get_ipython()
446 ip.user_ns.pop('run', None)
446 ip.user_ns.pop('run', None)
447
447
448 ip.run_cell(dedent("""\
448 ip.run_cell(dedent("""\
449 %%time
449 %%time
450 a = "ho"
450 a = "ho"
451 b = "hey"
451 b = "hey"
452 a+b
452 a+b
453 """
453 """
454 )
454 )
455 )
455 )
456 assert ip.user_ns_hidden["_"] == "hohey"
456 assert ip.user_ns_hidden["_"] == "hohey"
457
457
458
458
459 def test_time_local_ns():
459 def test_time_local_ns():
460 """
460 """
461 Test that local_ns is actually global_ns when running a cell magic
461 Test that local_ns is actually global_ns when running a cell magic
462 """
462 """
463 ip = get_ipython()
463 ip = get_ipython()
464 ip.run_cell("%%time\n" "myvar = 1")
464 ip.run_cell("%%time\n" "myvar = 1")
465 assert ip.user_ns["myvar"] == 1
465 assert ip.user_ns["myvar"] == 1
466 del ip.user_ns["myvar"]
466 del ip.user_ns["myvar"]
467
467
468
468
469 def test_doctest_mode():
469 def test_doctest_mode():
470 "Toggle doctest_mode twice, it should be a no-op and run without error"
470 "Toggle doctest_mode twice, it should be a no-op and run without error"
471 _ip.magic('doctest_mode')
471 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
473
473
474
474
475 def test_parse_options():
475 def test_parse_options():
476 """Tests for basic options parsing in magics."""
476 """Tests for basic options parsing in magics."""
477 # These are only the most minimal of tests, more should be added later. At
477 # These are only the most minimal of tests, more should be added later. At
478 # the very least we check that basic text/unicode calls work OK.
478 # the very least we check that basic text/unicode calls work OK.
479 m = DummyMagics(_ip)
479 m = DummyMagics(_ip)
480 assert m.parse_options("foo", "")[1] == "foo"
480 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
482
482
483
483
484 def test_parse_options_preserve_non_option_string():
484 def test_parse_options_preserve_non_option_string():
485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
486 m = DummyMagics(_ip)
486 m = DummyMagics(_ip)
487 opts, stmt = m.parse_options(
487 opts, stmt = m.parse_options(
488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
489 )
489 )
490 assert opts == {"n": "1", "r": "13"}
490 assert opts == {"n": "1", "r": "13"}
491 assert stmt == "_ = 314 + foo"
491 assert stmt == "_ = 314 + foo"
492
492
493
493
494 def test_run_magic_preserve_code_block():
494 def test_run_magic_preserve_code_block():
495 """Test to assert preservation of non-option part of magic-block, while running magic."""
495 """Test to assert preservation of non-option part of magic-block, while running magic."""
496 _ip.user_ns["spaces"] = []
496 _ip.user_ns["spaces"] = []
497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
498 assert _ip.user_ns["spaces"] == [[0]]
498 assert _ip.user_ns["spaces"] == [[0]]
499
499
500
500
501 def test_dirops():
501 def test_dirops():
502 """Test various directory handling operations."""
502 """Test various directory handling operations."""
503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
504 curpath = os.getcwd
504 curpath = os.getcwd
505 startdir = os.getcwd()
505 startdir = os.getcwd()
506 ipdir = os.path.realpath(_ip.ipython_dir)
506 ipdir = os.path.realpath(_ip.ipython_dir)
507 try:
507 try:
508 _ip.magic('cd "%s"' % ipdir)
508 _ip.magic('cd "%s"' % ipdir)
509 assert curpath() == ipdir
509 assert curpath() == ipdir
510 _ip.magic('cd -')
510 _ip.magic('cd -')
511 assert curpath() == startdir
511 assert curpath() == startdir
512 _ip.magic('pushd "%s"' % ipdir)
512 _ip.magic('pushd "%s"' % ipdir)
513 assert curpath() == ipdir
513 assert curpath() == ipdir
514 _ip.magic('popd')
514 _ip.magic('popd')
515 assert curpath() == startdir
515 assert curpath() == startdir
516 finally:
516 finally:
517 os.chdir(startdir)
517 os.chdir(startdir)
518
518
519
519
520 def test_cd_force_quiet():
520 def test_cd_force_quiet():
521 """Test OSMagics.cd_force_quiet option"""
521 """Test OSMagics.cd_force_quiet option"""
522 _ip.config.OSMagics.cd_force_quiet = True
522 _ip.config.OSMagics.cd_force_quiet = True
523 osmagics = osm.OSMagics(shell=_ip)
523 osmagics = osm.OSMagics(shell=_ip)
524
524
525 startdir = os.getcwd()
525 startdir = os.getcwd()
526 ipdir = os.path.realpath(_ip.ipython_dir)
526 ipdir = os.path.realpath(_ip.ipython_dir)
527
527
528 try:
528 try:
529 with tt.AssertNotPrints(ipdir):
529 with tt.AssertNotPrints(ipdir):
530 osmagics.cd('"%s"' % ipdir)
530 osmagics.cd('"%s"' % ipdir)
531 with tt.AssertNotPrints(startdir):
531 with tt.AssertNotPrints(startdir):
532 osmagics.cd('-')
532 osmagics.cd('-')
533 finally:
533 finally:
534 os.chdir(startdir)
534 os.chdir(startdir)
535
535
536
536
537 def test_xmode():
537 def test_xmode():
538 # Calling xmode three times should be a no-op
538 # Calling xmode three times should be a no-op
539 xmode = _ip.InteractiveTB.mode
539 xmode = _ip.InteractiveTB.mode
540 for i in range(4):
540 for i in range(4):
541 _ip.magic("xmode")
541 _ip.magic("xmode")
542 assert _ip.InteractiveTB.mode == xmode
542 assert _ip.InteractiveTB.mode == xmode
543
543
544 def test_reset_hard():
544 def test_reset_hard():
545 monitor = []
545 monitor = []
546 class A(object):
546 class A(object):
547 def __del__(self):
547 def __del__(self):
548 monitor.append(1)
548 monitor.append(1)
549 def __repr__(self):
549 def __repr__(self):
550 return "<A instance>"
550 return "<A instance>"
551
551
552 _ip.user_ns["a"] = A()
552 _ip.user_ns["a"] = A()
553 _ip.run_cell("a")
553 _ip.run_cell("a")
554
554
555 assert monitor == []
555 assert monitor == []
556 _ip.magic("reset -f")
556 _ip.magic("reset -f")
557 assert monitor == [1]
557 assert monitor == [1]
558
558
559 class TestXdel(tt.TempFileMixin):
559 class TestXdel(tt.TempFileMixin):
560 def test_xdel(self):
560 def test_xdel(self):
561 """Test that references from %run are cleared by xdel."""
561 """Test that references from %run are cleared by xdel."""
562 src = ("class A(object):\n"
562 src = ("class A(object):\n"
563 " monitor = []\n"
563 " monitor = []\n"
564 " def __del__(self):\n"
564 " def __del__(self):\n"
565 " self.monitor.append(1)\n"
565 " self.monitor.append(1)\n"
566 "a = A()\n")
566 "a = A()\n")
567 self.mktmp(src)
567 self.mktmp(src)
568 # %run creates some hidden references...
568 # %run creates some hidden references...
569 _ip.magic("run %s" % self.fname)
569 _ip.magic("run %s" % self.fname)
570 # ... as does the displayhook.
570 # ... as does the displayhook.
571 _ip.run_cell("a")
571 _ip.run_cell("a")
572
572
573 monitor = _ip.user_ns["A"].monitor
573 monitor = _ip.user_ns["A"].monitor
574 assert monitor == []
574 assert monitor == []
575
575
576 _ip.magic("xdel a")
576 _ip.magic("xdel a")
577
577
578 # Check that a's __del__ method has been called.
578 # Check that a's __del__ method has been called.
579 assert monitor == [1]
579 assert monitor == [1]
580
580
581 def doctest_who():
581 def doctest_who():
582 """doctest for %who
582 """doctest for %who
583
583
584 In [1]: %reset -sf
584 In [1]: %reset -sf
585
585
586 In [2]: alpha = 123
586 In [2]: alpha = 123
587
587
588 In [3]: beta = 'beta'
588 In [3]: beta = 'beta'
589
589
590 In [4]: %who int
590 In [4]: %who int
591 alpha
591 alpha
592
592
593 In [5]: %who str
593 In [5]: %who str
594 beta
594 beta
595
595
596 In [6]: %whos
596 In [6]: %whos
597 Variable Type Data/Info
597 Variable Type Data/Info
598 ----------------------------
598 ----------------------------
599 alpha int 123
599 alpha int 123
600 beta str beta
600 beta str beta
601
601
602 In [7]: %who_ls
602 In [7]: %who_ls
603 Out[7]: ['alpha', 'beta']
603 Out[7]: ['alpha', 'beta']
604 """
604 """
605
605
606 def test_whos():
606 def test_whos():
607 """Check that whos is protected against objects where repr() fails."""
607 """Check that whos is protected against objects where repr() fails."""
608 class A(object):
608 class A(object):
609 def __repr__(self):
609 def __repr__(self):
610 raise Exception()
610 raise Exception()
611 _ip.user_ns['a'] = A()
611 _ip.user_ns['a'] = A()
612 _ip.magic("whos")
612 _ip.magic("whos")
613
613
614 def doctest_precision():
614 def doctest_precision():
615 """doctest for %precision
615 """doctest for %precision
616
616
617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
618
618
619 In [2]: %precision 5
619 In [2]: %precision 5
620 Out[2]: '%.5f'
620 Out[2]: '%.5f'
621
621
622 In [3]: f.float_format
622 In [3]: f.float_format
623 Out[3]: '%.5f'
623 Out[3]: '%.5f'
624
624
625 In [4]: %precision %e
625 In [4]: %precision %e
626 Out[4]: '%e'
626 Out[4]: '%e'
627
627
628 In [5]: f(3.1415927)
628 In [5]: f(3.1415927)
629 Out[5]: '3.141593e+00'
629 Out[5]: '3.141593e+00'
630 """
630 """
631
631
632 def test_debug_magic():
632 def test_debug_magic():
633 """Test debugging a small code with %debug
633 """Test debugging a small code with %debug
634
634
635 In [1]: with PdbTestInput(['c']):
635 In [1]: with PdbTestInput(['c']):
636 ...: %debug print("a b") #doctest: +ELLIPSIS
636 ...: %debug print("a b") #doctest: +ELLIPSIS
637 ...:
637 ...:
638 ...
638 ...
639 ipdb> c
639 ipdb> c
640 a b
640 a b
641 In [2]:
641 In [2]:
642 """
642 """
643
643
644 def test_psearch():
644 def test_psearch():
645 with tt.AssertPrints("dict.fromkeys"):
645 with tt.AssertPrints("dict.fromkeys"):
646 _ip.run_cell("dict.fr*?")
646 _ip.run_cell("dict.fr*?")
647 with tt.AssertPrints("Ο€.is_integer"):
647 with tt.AssertPrints("Ο€.is_integer"):
648 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
648 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
649
649
650 def test_timeit_shlex():
650 def test_timeit_shlex():
651 """test shlex issues with timeit (#1109)"""
651 """test shlex issues with timeit (#1109)"""
652 _ip.ex("def f(*a,**kw): pass")
652 _ip.ex("def f(*a,**kw): pass")
653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
659
659
660
660
661 def test_timeit_special_syntax():
661 def test_timeit_special_syntax():
662 "Test %%timeit with IPython special syntax"
662 "Test %%timeit with IPython special syntax"
663 @register_line_magic
663 @register_line_magic
664 def lmagic(line):
664 def lmagic(line):
665 ip = get_ipython()
665 ip = get_ipython()
666 ip.user_ns['lmagic_out'] = line
666 ip.user_ns['lmagic_out'] = line
667
667
668 # line mode test
668 # line mode test
669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
670 assert _ip.user_ns["lmagic_out"] == "my line"
670 assert _ip.user_ns["lmagic_out"] == "my line"
671 # cell mode test
671 # cell mode test
672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
673 assert _ip.user_ns["lmagic_out"] == "my line2"
673 assert _ip.user_ns["lmagic_out"] == "my line2"
674
674
675
675
676 def test_timeit_return():
676 def test_timeit_return():
677 """
677 """
678 test whether timeit -o return object
678 test whether timeit -o return object
679 """
679 """
680
680
681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
682 assert(res is not None)
682 assert(res is not None)
683
683
684 def test_timeit_quiet():
684 def test_timeit_quiet():
685 """
685 """
686 test quiet option of timeit magic
686 test quiet option of timeit magic
687 """
687 """
688 with tt.AssertNotPrints("loops"):
688 with tt.AssertNotPrints("loops"):
689 _ip.run_cell("%timeit -n1 -r1 -q 1")
689 _ip.run_cell("%timeit -n1 -r1 -q 1")
690
690
691 def test_timeit_return_quiet():
691 def test_timeit_return_quiet():
692 with tt.AssertNotPrints("loops"):
692 with tt.AssertNotPrints("loops"):
693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
694 assert (res is not None)
694 assert (res is not None)
695
695
696 def test_timeit_invalid_return():
696 def test_timeit_invalid_return():
697 with pytest.raises(SyntaxError):
697 with pytest.raises(SyntaxError):
698 _ip.run_line_magic('timeit', 'return')
698 _ip.run_line_magic('timeit', 'return')
699
699
700 @dec.skipif(execution.profile is None)
700 @dec.skipif(execution.profile is None)
701 def test_prun_special_syntax():
701 def test_prun_special_syntax():
702 "Test %%prun with IPython special syntax"
702 "Test %%prun with IPython special syntax"
703 @register_line_magic
703 @register_line_magic
704 def lmagic(line):
704 def lmagic(line):
705 ip = get_ipython()
705 ip = get_ipython()
706 ip.user_ns['lmagic_out'] = line
706 ip.user_ns['lmagic_out'] = line
707
707
708 # line mode test
708 # line mode test
709 _ip.run_line_magic("prun", "-q %lmagic my line")
709 _ip.run_line_magic("prun", "-q %lmagic my line")
710 assert _ip.user_ns["lmagic_out"] == "my line"
710 assert _ip.user_ns["lmagic_out"] == "my line"
711 # cell mode test
711 # cell mode test
712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
713 assert _ip.user_ns["lmagic_out"] == "my line2"
713 assert _ip.user_ns["lmagic_out"] == "my line2"
714
714
715
715
716 @dec.skipif(execution.profile is None)
716 @dec.skipif(execution.profile is None)
717 def test_prun_quotes():
717 def test_prun_quotes():
718 "Test that prun does not clobber string escapes (GH #1302)"
718 "Test that prun does not clobber string escapes (GH #1302)"
719 _ip.magic(r"prun -q x = '\t'")
719 _ip.magic(r"prun -q x = '\t'")
720 assert _ip.user_ns["x"] == "\t"
720 assert _ip.user_ns["x"] == "\t"
721
721
722
722
723 def test_extension():
723 def test_extension():
724 # Debugging information for failures of this test
724 # Debugging information for failures of this test
725 print('sys.path:')
725 print('sys.path:')
726 for p in sys.path:
726 for p in sys.path:
727 print(' ', p)
727 print(' ', p)
728 print('CWD', os.getcwd())
728 print('CWD', os.getcwd())
729
729
730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
732 sys.path.insert(0, daft_path)
732 sys.path.insert(0, daft_path)
733 try:
733 try:
734 _ip.user_ns.pop('arq', None)
734 _ip.user_ns.pop('arq', None)
735 invalidate_caches() # Clear import caches
735 invalidate_caches() # Clear import caches
736 _ip.magic("load_ext daft_extension")
736 _ip.magic("load_ext daft_extension")
737 assert _ip.user_ns["arq"] == 185
737 assert _ip.user_ns["arq"] == 185
738 _ip.magic("unload_ext daft_extension")
738 _ip.magic("unload_ext daft_extension")
739 assert 'arq' not in _ip.user_ns
739 assert 'arq' not in _ip.user_ns
740 finally:
740 finally:
741 sys.path.remove(daft_path)
741 sys.path.remove(daft_path)
742
742
743
743
744 def test_notebook_export_json():
744 def test_notebook_export_json():
745 pytest.importorskip("nbformat")
745 _ip = get_ipython()
746 _ip = get_ipython()
746 _ip.history_manager.reset() # Clear any existing history.
747 _ip.history_manager.reset() # Clear any existing history.
747 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
748 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
748 for i, cmd in enumerate(cmds, start=1):
749 for i, cmd in enumerate(cmds, start=1):
749 _ip.history_manager.store_inputs(i, cmd)
750 _ip.history_manager.store_inputs(i, cmd)
750 with TemporaryDirectory() as td:
751 with TemporaryDirectory() as td:
751 outfile = os.path.join(td, "nb.ipynb")
752 outfile = os.path.join(td, "nb.ipynb")
752 _ip.magic("notebook -e %s" % outfile)
753 _ip.magic("notebook -e %s" % outfile)
753
754
754
755
755 class TestEnv(TestCase):
756 class TestEnv(TestCase):
756
757
757 def test_env(self):
758 def test_env(self):
758 env = _ip.magic("env")
759 env = _ip.magic("env")
759 self.assertTrue(isinstance(env, dict))
760 self.assertTrue(isinstance(env, dict))
760
761
761 def test_env_secret(self):
762 def test_env_secret(self):
762 env = _ip.magic("env")
763 env = _ip.magic("env")
763 hidden = "<hidden>"
764 hidden = "<hidden>"
764 with mock.patch.dict(
765 with mock.patch.dict(
765 os.environ,
766 os.environ,
766 {
767 {
767 "API_KEY": "abc123",
768 "API_KEY": "abc123",
768 "SECRET_THING": "ssshhh",
769 "SECRET_THING": "ssshhh",
769 "JUPYTER_TOKEN": "",
770 "JUPYTER_TOKEN": "",
770 "VAR": "abc"
771 "VAR": "abc"
771 }
772 }
772 ):
773 ):
773 env = _ip.magic("env")
774 env = _ip.magic("env")
774 assert env["API_KEY"] == hidden
775 assert env["API_KEY"] == hidden
775 assert env["SECRET_THING"] == hidden
776 assert env["SECRET_THING"] == hidden
776 assert env["JUPYTER_TOKEN"] == hidden
777 assert env["JUPYTER_TOKEN"] == hidden
777 assert env["VAR"] == "abc"
778 assert env["VAR"] == "abc"
778
779
779 def test_env_get_set_simple(self):
780 def test_env_get_set_simple(self):
780 env = _ip.magic("env var val1")
781 env = _ip.magic("env var val1")
781 self.assertEqual(env, None)
782 self.assertEqual(env, None)
782 self.assertEqual(os.environ['var'], 'val1')
783 self.assertEqual(os.environ['var'], 'val1')
783 self.assertEqual(_ip.magic("env var"), 'val1')
784 self.assertEqual(_ip.magic("env var"), 'val1')
784 env = _ip.magic("env var=val2")
785 env = _ip.magic("env var=val2")
785 self.assertEqual(env, None)
786 self.assertEqual(env, None)
786 self.assertEqual(os.environ['var'], 'val2')
787 self.assertEqual(os.environ['var'], 'val2')
787
788
788 def test_env_get_set_complex(self):
789 def test_env_get_set_complex(self):
789 env = _ip.magic("env var 'val1 '' 'val2")
790 env = _ip.magic("env var 'val1 '' 'val2")
790 self.assertEqual(env, None)
791 self.assertEqual(env, None)
791 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
792 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
792 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
793 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
793 env = _ip.magic('env var=val2 val3="val4')
794 env = _ip.magic('env var=val2 val3="val4')
794 self.assertEqual(env, None)
795 self.assertEqual(env, None)
795 self.assertEqual(os.environ['var'], 'val2 val3="val4')
796 self.assertEqual(os.environ['var'], 'val2 val3="val4')
796
797
797 def test_env_set_bad_input(self):
798 def test_env_set_bad_input(self):
798 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
799 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
799
800
800 def test_env_set_whitespace(self):
801 def test_env_set_whitespace(self):
801 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
802 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
802
803
803
804
804 class CellMagicTestCase(TestCase):
805 class CellMagicTestCase(TestCase):
805
806
806 def check_ident(self, magic):
807 def check_ident(self, magic):
807 # Manually called, we get the result
808 # Manually called, we get the result
808 out = _ip.run_cell_magic(magic, "a", "b")
809 out = _ip.run_cell_magic(magic, "a", "b")
809 assert out == ("a", "b")
810 assert out == ("a", "b")
810 # Via run_cell, it goes into the user's namespace via displayhook
811 # Via run_cell, it goes into the user's namespace via displayhook
811 _ip.run_cell("%%" + magic + " c\nd\n")
812 _ip.run_cell("%%" + magic + " c\nd\n")
812 assert _ip.user_ns["_"] == ("c", "d\n")
813 assert _ip.user_ns["_"] == ("c", "d\n")
813
814
814 def test_cell_magic_func_deco(self):
815 def test_cell_magic_func_deco(self):
815 "Cell magic using simple decorator"
816 "Cell magic using simple decorator"
816 @register_cell_magic
817 @register_cell_magic
817 def cellm(line, cell):
818 def cellm(line, cell):
818 return line, cell
819 return line, cell
819
820
820 self.check_ident('cellm')
821 self.check_ident('cellm')
821
822
822 def test_cell_magic_reg(self):
823 def test_cell_magic_reg(self):
823 "Cell magic manually registered"
824 "Cell magic manually registered"
824 def cellm(line, cell):
825 def cellm(line, cell):
825 return line, cell
826 return line, cell
826
827
827 _ip.register_magic_function(cellm, 'cell', 'cellm2')
828 _ip.register_magic_function(cellm, 'cell', 'cellm2')
828 self.check_ident('cellm2')
829 self.check_ident('cellm2')
829
830
830 def test_cell_magic_class(self):
831 def test_cell_magic_class(self):
831 "Cell magics declared via a class"
832 "Cell magics declared via a class"
832 @magics_class
833 @magics_class
833 class MyMagics(Magics):
834 class MyMagics(Magics):
834
835
835 @cell_magic
836 @cell_magic
836 def cellm3(self, line, cell):
837 def cellm3(self, line, cell):
837 return line, cell
838 return line, cell
838
839
839 _ip.register_magics(MyMagics)
840 _ip.register_magics(MyMagics)
840 self.check_ident('cellm3')
841 self.check_ident('cellm3')
841
842
842 def test_cell_magic_class2(self):
843 def test_cell_magic_class2(self):
843 "Cell magics declared via a class, #2"
844 "Cell magics declared via a class, #2"
844 @magics_class
845 @magics_class
845 class MyMagics2(Magics):
846 class MyMagics2(Magics):
846
847
847 @cell_magic('cellm4')
848 @cell_magic('cellm4')
848 def cellm33(self, line, cell):
849 def cellm33(self, line, cell):
849 return line, cell
850 return line, cell
850
851
851 _ip.register_magics(MyMagics2)
852 _ip.register_magics(MyMagics2)
852 self.check_ident('cellm4')
853 self.check_ident('cellm4')
853 # Check that nothing is registered as 'cellm33'
854 # Check that nothing is registered as 'cellm33'
854 c33 = _ip.find_cell_magic('cellm33')
855 c33 = _ip.find_cell_magic('cellm33')
855 assert c33 == None
856 assert c33 == None
856
857
857 def test_file():
858 def test_file():
858 """Basic %%writefile"""
859 """Basic %%writefile"""
859 ip = get_ipython()
860 ip = get_ipython()
860 with TemporaryDirectory() as td:
861 with TemporaryDirectory() as td:
861 fname = os.path.join(td, 'file1')
862 fname = os.path.join(td, 'file1')
862 ip.run_cell_magic("writefile", fname, u'\n'.join([
863 ip.run_cell_magic("writefile", fname, u'\n'.join([
863 'line1',
864 'line1',
864 'line2',
865 'line2',
865 ]))
866 ]))
866 s = Path(fname).read_text()
867 s = Path(fname).read_text()
867 assert "line1\n" in s
868 assert "line1\n" in s
868 assert "line2" in s
869 assert "line2" in s
869
870
870
871
871 @dec.skip_win32
872 @dec.skip_win32
872 def test_file_single_quote():
873 def test_file_single_quote():
873 """Basic %%writefile with embedded single quotes"""
874 """Basic %%writefile with embedded single quotes"""
874 ip = get_ipython()
875 ip = get_ipython()
875 with TemporaryDirectory() as td:
876 with TemporaryDirectory() as td:
876 fname = os.path.join(td, '\'file1\'')
877 fname = os.path.join(td, '\'file1\'')
877 ip.run_cell_magic("writefile", fname, u'\n'.join([
878 ip.run_cell_magic("writefile", fname, u'\n'.join([
878 'line1',
879 'line1',
879 'line2',
880 'line2',
880 ]))
881 ]))
881 s = Path(fname).read_text()
882 s = Path(fname).read_text()
882 assert "line1\n" in s
883 assert "line1\n" in s
883 assert "line2" in s
884 assert "line2" in s
884
885
885
886
886 @dec.skip_win32
887 @dec.skip_win32
887 def test_file_double_quote():
888 def test_file_double_quote():
888 """Basic %%writefile with embedded double quotes"""
889 """Basic %%writefile with embedded double quotes"""
889 ip = get_ipython()
890 ip = get_ipython()
890 with TemporaryDirectory() as td:
891 with TemporaryDirectory() as td:
891 fname = os.path.join(td, '"file1"')
892 fname = os.path.join(td, '"file1"')
892 ip.run_cell_magic("writefile", fname, u'\n'.join([
893 ip.run_cell_magic("writefile", fname, u'\n'.join([
893 'line1',
894 'line1',
894 'line2',
895 'line2',
895 ]))
896 ]))
896 s = Path(fname).read_text()
897 s = Path(fname).read_text()
897 assert "line1\n" in s
898 assert "line1\n" in s
898 assert "line2" in s
899 assert "line2" in s
899
900
900
901
901 def test_file_var_expand():
902 def test_file_var_expand():
902 """%%writefile $filename"""
903 """%%writefile $filename"""
903 ip = get_ipython()
904 ip = get_ipython()
904 with TemporaryDirectory() as td:
905 with TemporaryDirectory() as td:
905 fname = os.path.join(td, 'file1')
906 fname = os.path.join(td, 'file1')
906 ip.user_ns['filename'] = fname
907 ip.user_ns['filename'] = fname
907 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
908 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
908 'line1',
909 'line1',
909 'line2',
910 'line2',
910 ]))
911 ]))
911 s = Path(fname).read_text()
912 s = Path(fname).read_text()
912 assert "line1\n" in s
913 assert "line1\n" in s
913 assert "line2" in s
914 assert "line2" in s
914
915
915
916
916 def test_file_unicode():
917 def test_file_unicode():
917 """%%writefile with unicode cell"""
918 """%%writefile with unicode cell"""
918 ip = get_ipython()
919 ip = get_ipython()
919 with TemporaryDirectory() as td:
920 with TemporaryDirectory() as td:
920 fname = os.path.join(td, 'file1')
921 fname = os.path.join(td, 'file1')
921 ip.run_cell_magic("writefile", fname, u'\n'.join([
922 ip.run_cell_magic("writefile", fname, u'\n'.join([
922 u'linΓ©1',
923 u'linΓ©1',
923 u'linΓ©2',
924 u'linΓ©2',
924 ]))
925 ]))
925 with io.open(fname, encoding='utf-8') as f:
926 with io.open(fname, encoding='utf-8') as f:
926 s = f.read()
927 s = f.read()
927 assert "linΓ©1\n" in s
928 assert "linΓ©1\n" in s
928 assert "linΓ©2" in s
929 assert "linΓ©2" in s
929
930
930
931
931 def test_file_amend():
932 def test_file_amend():
932 """%%writefile -a amends files"""
933 """%%writefile -a amends files"""
933 ip = get_ipython()
934 ip = get_ipython()
934 with TemporaryDirectory() as td:
935 with TemporaryDirectory() as td:
935 fname = os.path.join(td, 'file2')
936 fname = os.path.join(td, 'file2')
936 ip.run_cell_magic("writefile", fname, u'\n'.join([
937 ip.run_cell_magic("writefile", fname, u'\n'.join([
937 'line1',
938 'line1',
938 'line2',
939 'line2',
939 ]))
940 ]))
940 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
941 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
941 'line3',
942 'line3',
942 'line4',
943 'line4',
943 ]))
944 ]))
944 s = Path(fname).read_text()
945 s = Path(fname).read_text()
945 assert "line1\n" in s
946 assert "line1\n" in s
946 assert "line3\n" in s
947 assert "line3\n" in s
947
948
948
949
949 def test_file_spaces():
950 def test_file_spaces():
950 """%%file with spaces in filename"""
951 """%%file with spaces in filename"""
951 ip = get_ipython()
952 ip = get_ipython()
952 with TemporaryWorkingDirectory() as td:
953 with TemporaryWorkingDirectory() as td:
953 fname = "file name"
954 fname = "file name"
954 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
955 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
955 'line1',
956 'line1',
956 'line2',
957 'line2',
957 ]))
958 ]))
958 s = Path(fname).read_text()
959 s = Path(fname).read_text()
959 assert "line1\n" in s
960 assert "line1\n" in s
960 assert "line2" in s
961 assert "line2" in s
961
962
962
963
963 def test_script_config():
964 def test_script_config():
964 ip = get_ipython()
965 ip = get_ipython()
965 ip.config.ScriptMagics.script_magics = ['whoda']
966 ip.config.ScriptMagics.script_magics = ['whoda']
966 sm = script.ScriptMagics(shell=ip)
967 sm = script.ScriptMagics(shell=ip)
967 assert "whoda" in sm.magics["cell"]
968 assert "whoda" in sm.magics["cell"]
968
969
969
970
970 @pytest.fixture
971 @pytest.fixture
971 def event_loop():
972 def event_loop():
972 yield asyncio.get_event_loop_policy().get_event_loop()
973 yield asyncio.get_event_loop_policy().get_event_loop()
973
974
974
975
975 @dec.skip_iptest_but_not_pytest
976 @dec.skip_iptest_but_not_pytest
976 @dec.skip_win32
977 @dec.skip_win32
977 @pytest.mark.skipif(
978 @pytest.mark.skipif(
978 sys.platform == "win32", reason="This test does not run under Windows"
979 sys.platform == "win32", reason="This test does not run under Windows"
979 )
980 )
980 def test_script_out(event_loop):
981 def test_script_out(event_loop):
981 assert event_loop.is_running() is False
982 assert event_loop.is_running() is False
982
983
983 ip = get_ipython()
984 ip = get_ipython()
984 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
985 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
985 assert event_loop.is_running() is False
986 assert event_loop.is_running() is False
986 assert ip.user_ns["output"] == "hi\n"
987 assert ip.user_ns["output"] == "hi\n"
987
988
988
989
989 @dec.skip_iptest_but_not_pytest
990 @dec.skip_iptest_but_not_pytest
990 @dec.skip_win32
991 @dec.skip_win32
991 @pytest.mark.skipif(
992 @pytest.mark.skipif(
992 sys.platform == "win32", reason="This test does not run under Windows"
993 sys.platform == "win32", reason="This test does not run under Windows"
993 )
994 )
994 def test_script_err(event_loop):
995 def test_script_err(event_loop):
995 ip = get_ipython()
996 ip = get_ipython()
996 assert event_loop.is_running() is False
997 assert event_loop.is_running() is False
997 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
998 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
998 assert event_loop.is_running() is False
999 assert event_loop.is_running() is False
999 assert ip.user_ns["error"] == "hello\n"
1000 assert ip.user_ns["error"] == "hello\n"
1000
1001
1001
1002
1002 @dec.skip_iptest_but_not_pytest
1003 @dec.skip_iptest_but_not_pytest
1003 @dec.skip_win32
1004 @dec.skip_win32
1004 @pytest.mark.skipif(
1005 @pytest.mark.skipif(
1005 sys.platform == "win32", reason="This test does not run under Windows"
1006 sys.platform == "win32", reason="This test does not run under Windows"
1006 )
1007 )
1007 def test_script_out_err():
1008 def test_script_out_err():
1008
1009
1009 ip = get_ipython()
1010 ip = get_ipython()
1010 ip.run_cell_magic(
1011 ip.run_cell_magic(
1011 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1012 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1012 )
1013 )
1013 assert ip.user_ns["output"] == "hi\n"
1014 assert ip.user_ns["output"] == "hi\n"
1014 assert ip.user_ns["error"] == "hello\n"
1015 assert ip.user_ns["error"] == "hello\n"
1015
1016
1016
1017
1017 @dec.skip_iptest_but_not_pytest
1018 @dec.skip_iptest_but_not_pytest
1018 @dec.skip_win32
1019 @dec.skip_win32
1019 @pytest.mark.skipif(
1020 @pytest.mark.skipif(
1020 sys.platform == "win32", reason="This test does not run under Windows"
1021 sys.platform == "win32", reason="This test does not run under Windows"
1021 )
1022 )
1022 async def test_script_bg_out(event_loop):
1023 async def test_script_bg_out(event_loop):
1023 ip = get_ipython()
1024 ip = get_ipython()
1024 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1025 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1025 assert (await ip.user_ns["output"].read()) == b"hi\n"
1026 assert (await ip.user_ns["output"].read()) == b"hi\n"
1026 ip.user_ns["output"].close()
1027 ip.user_ns["output"].close()
1027 event_loop.stop()
1028 event_loop.stop()
1028
1029
1029
1030
1030 @dec.skip_iptest_but_not_pytest
1031 @dec.skip_iptest_but_not_pytest
1031 @dec.skip_win32
1032 @dec.skip_win32
1032 @pytest.mark.skipif(
1033 @pytest.mark.skipif(
1033 sys.platform == "win32", reason="This test does not run under Windows"
1034 sys.platform == "win32", reason="This test does not run under Windows"
1034 )
1035 )
1035 async def test_script_bg_err():
1036 async def test_script_bg_err():
1036 ip = get_ipython()
1037 ip = get_ipython()
1037 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1038 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1038 assert (await ip.user_ns["error"].read()) == b"hello\n"
1039 assert (await ip.user_ns["error"].read()) == b"hello\n"
1039 ip.user_ns["error"].close()
1040 ip.user_ns["error"].close()
1040
1041
1041
1042
1042 @dec.skip_iptest_but_not_pytest
1043 @dec.skip_iptest_but_not_pytest
1043 @dec.skip_win32
1044 @dec.skip_win32
1044 @pytest.mark.skipif(
1045 @pytest.mark.skipif(
1045 sys.platform == "win32", reason="This test does not run under Windows"
1046 sys.platform == "win32", reason="This test does not run under Windows"
1046 )
1047 )
1047 async def test_script_bg_out_err():
1048 async def test_script_bg_out_err():
1048 ip = get_ipython()
1049 ip = get_ipython()
1049 ip.run_cell_magic(
1050 ip.run_cell_magic(
1050 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1051 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1051 )
1052 )
1052 assert (await ip.user_ns["output"].read()) == b"hi\n"
1053 assert (await ip.user_ns["output"].read()) == b"hi\n"
1053 assert (await ip.user_ns["error"].read()) == b"hello\n"
1054 assert (await ip.user_ns["error"].read()) == b"hello\n"
1054 ip.user_ns["output"].close()
1055 ip.user_ns["output"].close()
1055 ip.user_ns["error"].close()
1056 ip.user_ns["error"].close()
1056
1057
1057
1058
1058 def test_script_defaults():
1059 def test_script_defaults():
1059 ip = get_ipython()
1060 ip = get_ipython()
1060 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1061 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1061 try:
1062 try:
1062 find_cmd(cmd)
1063 find_cmd(cmd)
1063 except Exception:
1064 except Exception:
1064 pass
1065 pass
1065 else:
1066 else:
1066 assert cmd in ip.magics_manager.magics["cell"]
1067 assert cmd in ip.magics_manager.magics["cell"]
1067
1068
1068
1069
1069 @magics_class
1070 @magics_class
1070 class FooFoo(Magics):
1071 class FooFoo(Magics):
1071 """class with both %foo and %%foo magics"""
1072 """class with both %foo and %%foo magics"""
1072 @line_magic('foo')
1073 @line_magic('foo')
1073 def line_foo(self, line):
1074 def line_foo(self, line):
1074 "I am line foo"
1075 "I am line foo"
1075 pass
1076 pass
1076
1077
1077 @cell_magic("foo")
1078 @cell_magic("foo")
1078 def cell_foo(self, line, cell):
1079 def cell_foo(self, line, cell):
1079 "I am cell foo, not line foo"
1080 "I am cell foo, not line foo"
1080 pass
1081 pass
1081
1082
1082 def test_line_cell_info():
1083 def test_line_cell_info():
1083 """%%foo and %foo magics are distinguishable to inspect"""
1084 """%%foo and %foo magics are distinguishable to inspect"""
1084 ip = get_ipython()
1085 ip = get_ipython()
1085 ip.magics_manager.register(FooFoo)
1086 ip.magics_manager.register(FooFoo)
1086 oinfo = ip.object_inspect("foo")
1087 oinfo = ip.object_inspect("foo")
1087 assert oinfo["found"] is True
1088 assert oinfo["found"] is True
1088 assert oinfo["ismagic"] is True
1089 assert oinfo["ismagic"] is True
1089
1090
1090 oinfo = ip.object_inspect("%%foo")
1091 oinfo = ip.object_inspect("%%foo")
1091 assert oinfo["found"] is True
1092 assert oinfo["found"] is True
1092 assert oinfo["ismagic"] is True
1093 assert oinfo["ismagic"] is True
1093 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1094 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1094
1095
1095 oinfo = ip.object_inspect("%foo")
1096 oinfo = ip.object_inspect("%foo")
1096 assert oinfo["found"] is True
1097 assert oinfo["found"] is True
1097 assert oinfo["ismagic"] is True
1098 assert oinfo["ismagic"] is True
1098 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1099 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1099
1100
1100
1101
1101 def test_multiple_magics():
1102 def test_multiple_magics():
1102 ip = get_ipython()
1103 ip = get_ipython()
1103 foo1 = FooFoo(ip)
1104 foo1 = FooFoo(ip)
1104 foo2 = FooFoo(ip)
1105 foo2 = FooFoo(ip)
1105 mm = ip.magics_manager
1106 mm = ip.magics_manager
1106 mm.register(foo1)
1107 mm.register(foo1)
1107 assert mm.magics["line"]["foo"].__self__ is foo1
1108 assert mm.magics["line"]["foo"].__self__ is foo1
1108 mm.register(foo2)
1109 mm.register(foo2)
1109 assert mm.magics["line"]["foo"].__self__ is foo2
1110 assert mm.magics["line"]["foo"].__self__ is foo2
1110
1111
1111
1112
1112 def test_alias_magic():
1113 def test_alias_magic():
1113 """Test %alias_magic."""
1114 """Test %alias_magic."""
1114 ip = get_ipython()
1115 ip = get_ipython()
1115 mm = ip.magics_manager
1116 mm = ip.magics_manager
1116
1117
1117 # Basic operation: both cell and line magics are created, if possible.
1118 # Basic operation: both cell and line magics are created, if possible.
1118 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1119 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1119 assert "timeit_alias" in mm.magics["line"]
1120 assert "timeit_alias" in mm.magics["line"]
1120 assert "timeit_alias" in mm.magics["cell"]
1121 assert "timeit_alias" in mm.magics["cell"]
1121
1122
1122 # --cell is specified, line magic not created.
1123 # --cell is specified, line magic not created.
1123 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1124 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1124 assert "timeit_cell_alias" not in mm.magics["line"]
1125 assert "timeit_cell_alias" not in mm.magics["line"]
1125 assert "timeit_cell_alias" in mm.magics["cell"]
1126 assert "timeit_cell_alias" in mm.magics["cell"]
1126
1127
1127 # Test that line alias is created successfully.
1128 # Test that line alias is created successfully.
1128 ip.run_line_magic("alias_magic", "--line env_alias env")
1129 ip.run_line_magic("alias_magic", "--line env_alias env")
1129 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1130 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1130
1131
1131 # Test that line alias with parameters passed in is created successfully.
1132 # Test that line alias with parameters passed in is created successfully.
1132 ip.run_line_magic(
1133 ip.run_line_magic(
1133 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1134 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1134 )
1135 )
1135 assert "history_alias" in mm.magics["line"]
1136 assert "history_alias" in mm.magics["line"]
1136
1137
1137
1138
1138 def test_save():
1139 def test_save():
1139 """Test %save."""
1140 """Test %save."""
1140 ip = get_ipython()
1141 ip = get_ipython()
1141 ip.history_manager.reset() # Clear any existing history.
1142 ip.history_manager.reset() # Clear any existing history.
1142 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1143 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1143 for i, cmd in enumerate(cmds, start=1):
1144 for i, cmd in enumerate(cmds, start=1):
1144 ip.history_manager.store_inputs(i, cmd)
1145 ip.history_manager.store_inputs(i, cmd)
1145 with TemporaryDirectory() as tmpdir:
1146 with TemporaryDirectory() as tmpdir:
1146 file = os.path.join(tmpdir, "testsave.py")
1147 file = os.path.join(tmpdir, "testsave.py")
1147 ip.run_line_magic("save", "%s 1-10" % file)
1148 ip.run_line_magic("save", "%s 1-10" % file)
1148 content = Path(file).read_text()
1149 content = Path(file).read_text()
1149 assert content.count(cmds[0]) == 1
1150 assert content.count(cmds[0]) == 1
1150 assert "coding: utf-8" in content
1151 assert "coding: utf-8" in content
1151 ip.run_line_magic("save", "-a %s 1-10" % file)
1152 ip.run_line_magic("save", "-a %s 1-10" % file)
1152 content = Path(file).read_text()
1153 content = Path(file).read_text()
1153 assert content.count(cmds[0]) == 2
1154 assert content.count(cmds[0]) == 2
1154 assert "coding: utf-8" in content
1155 assert "coding: utf-8" in content
1155
1156
1156
1157
1157 def test_save_with_no_args():
1158 def test_save_with_no_args():
1158 ip = get_ipython()
1159 ip = get_ipython()
1159 ip.history_manager.reset() # Clear any existing history.
1160 ip.history_manager.reset() # Clear any existing history.
1160 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1161 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1161 for i, cmd in enumerate(cmds, start=1):
1162 for i, cmd in enumerate(cmds, start=1):
1162 ip.history_manager.store_inputs(i, cmd)
1163 ip.history_manager.store_inputs(i, cmd)
1163
1164
1164 with TemporaryDirectory() as tmpdir:
1165 with TemporaryDirectory() as tmpdir:
1165 path = os.path.join(tmpdir, "testsave.py")
1166 path = os.path.join(tmpdir, "testsave.py")
1166 ip.run_line_magic("save", path)
1167 ip.run_line_magic("save", path)
1167 content = Path(path).read_text()
1168 content = Path(path).read_text()
1168 expected_content = dedent(
1169 expected_content = dedent(
1169 """\
1170 """\
1170 # coding: utf-8
1171 # coding: utf-8
1171 a=1
1172 a=1
1172 def b():
1173 def b():
1173 return a**2
1174 return a**2
1174 print(a, b())
1175 print(a, b())
1175 """
1176 """
1176 )
1177 )
1177 assert content == expected_content
1178 assert content == expected_content
1178
1179
1179
1180
1180 def test_store():
1181 def test_store():
1181 """Test %store."""
1182 """Test %store."""
1182 ip = get_ipython()
1183 ip = get_ipython()
1183 ip.run_line_magic('load_ext', 'storemagic')
1184 ip.run_line_magic('load_ext', 'storemagic')
1184
1185
1185 # make sure the storage is empty
1186 # make sure the storage is empty
1186 ip.run_line_magic("store", "-z")
1187 ip.run_line_magic("store", "-z")
1187 ip.user_ns["var"] = 42
1188 ip.user_ns["var"] = 42
1188 ip.run_line_magic("store", "var")
1189 ip.run_line_magic("store", "var")
1189 ip.user_ns["var"] = 39
1190 ip.user_ns["var"] = 39
1190 ip.run_line_magic("store", "-r")
1191 ip.run_line_magic("store", "-r")
1191 assert ip.user_ns["var"] == 42
1192 assert ip.user_ns["var"] == 42
1192
1193
1193 ip.run_line_magic("store", "-d var")
1194 ip.run_line_magic("store", "-d var")
1194 ip.user_ns["var"] = 39
1195 ip.user_ns["var"] = 39
1195 ip.run_line_magic("store", "-r")
1196 ip.run_line_magic("store", "-r")
1196 assert ip.user_ns["var"] == 39
1197 assert ip.user_ns["var"] == 39
1197
1198
1198
1199
1199 def _run_edit_test(arg_s, exp_filename=None,
1200 def _run_edit_test(arg_s, exp_filename=None,
1200 exp_lineno=-1,
1201 exp_lineno=-1,
1201 exp_contents=None,
1202 exp_contents=None,
1202 exp_is_temp=None):
1203 exp_is_temp=None):
1203 ip = get_ipython()
1204 ip = get_ipython()
1204 M = code.CodeMagics(ip)
1205 M = code.CodeMagics(ip)
1205 last_call = ['','']
1206 last_call = ['','']
1206 opts,args = M.parse_options(arg_s,'prxn:')
1207 opts,args = M.parse_options(arg_s,'prxn:')
1207 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1208 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1208
1209
1209 if exp_filename is not None:
1210 if exp_filename is not None:
1210 assert exp_filename == filename
1211 assert exp_filename == filename
1211 if exp_contents is not None:
1212 if exp_contents is not None:
1212 with io.open(filename, 'r', encoding='utf-8') as f:
1213 with io.open(filename, 'r', encoding='utf-8') as f:
1213 contents = f.read()
1214 contents = f.read()
1214 assert exp_contents == contents
1215 assert exp_contents == contents
1215 if exp_lineno != -1:
1216 if exp_lineno != -1:
1216 assert exp_lineno == lineno
1217 assert exp_lineno == lineno
1217 if exp_is_temp is not None:
1218 if exp_is_temp is not None:
1218 assert exp_is_temp == is_temp
1219 assert exp_is_temp == is_temp
1219
1220
1220
1221
1221 def test_edit_interactive():
1222 def test_edit_interactive():
1222 """%edit on interactively defined objects"""
1223 """%edit on interactively defined objects"""
1223 ip = get_ipython()
1224 ip = get_ipython()
1224 n = ip.execution_count
1225 n = ip.execution_count
1225 ip.run_cell("def foo(): return 1", store_history=True)
1226 ip.run_cell("def foo(): return 1", store_history=True)
1226
1227
1227 with pytest.raises(code.InteractivelyDefined) as e:
1228 with pytest.raises(code.InteractivelyDefined) as e:
1228 _run_edit_test("foo")
1229 _run_edit_test("foo")
1229 assert e.value.index == n
1230 assert e.value.index == n
1230
1231
1231
1232
1232 def test_edit_cell():
1233 def test_edit_cell():
1233 """%edit [cell id]"""
1234 """%edit [cell id]"""
1234 ip = get_ipython()
1235 ip = get_ipython()
1235
1236
1236 ip.run_cell("def foo(): return 1", store_history=True)
1237 ip.run_cell("def foo(): return 1", store_history=True)
1237
1238
1238 # test
1239 # test
1239 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1240 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1240
1241
1241 def test_edit_fname():
1242 def test_edit_fname():
1242 """%edit file"""
1243 """%edit file"""
1243 # test
1244 # test
1244 _run_edit_test("test file.py", exp_filename="test file.py")
1245 _run_edit_test("test file.py", exp_filename="test file.py")
1245
1246
1246 def test_bookmark():
1247 def test_bookmark():
1247 ip = get_ipython()
1248 ip = get_ipython()
1248 ip.run_line_magic('bookmark', 'bmname')
1249 ip.run_line_magic('bookmark', 'bmname')
1249 with tt.AssertPrints('bmname'):
1250 with tt.AssertPrints('bmname'):
1250 ip.run_line_magic('bookmark', '-l')
1251 ip.run_line_magic('bookmark', '-l')
1251 ip.run_line_magic('bookmark', '-d bmname')
1252 ip.run_line_magic('bookmark', '-d bmname')
1252
1253
1253 def test_ls_magic():
1254 def test_ls_magic():
1254 ip = get_ipython()
1255 ip = get_ipython()
1255 json_formatter = ip.display_formatter.formatters['application/json']
1256 json_formatter = ip.display_formatter.formatters['application/json']
1256 json_formatter.enabled = True
1257 json_formatter.enabled = True
1257 lsmagic = ip.magic('lsmagic')
1258 lsmagic = ip.magic('lsmagic')
1258 with warnings.catch_warnings(record=True) as w:
1259 with warnings.catch_warnings(record=True) as w:
1259 j = json_formatter(lsmagic)
1260 j = json_formatter(lsmagic)
1260 assert sorted(j) == ["cell", "line"]
1261 assert sorted(j) == ["cell", "line"]
1261 assert w == [] # no warnings
1262 assert w == [] # no warnings
1262
1263
1263
1264
1264 def test_strip_initial_indent():
1265 def test_strip_initial_indent():
1265 def sii(s):
1266 def sii(s):
1266 lines = s.splitlines()
1267 lines = s.splitlines()
1267 return '\n'.join(code.strip_initial_indent(lines))
1268 return '\n'.join(code.strip_initial_indent(lines))
1268
1269
1269 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1270 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1270 assert sii(" a\n b\nc") == "a\n b\nc"
1271 assert sii(" a\n b\nc") == "a\n b\nc"
1271 assert sii("a\n b") == "a\n b"
1272 assert sii("a\n b") == "a\n b"
1272
1273
1273 def test_logging_magic_quiet_from_arg():
1274 def test_logging_magic_quiet_from_arg():
1274 _ip.config.LoggingMagics.quiet = False
1275 _ip.config.LoggingMagics.quiet = False
1275 lm = logging.LoggingMagics(shell=_ip)
1276 lm = logging.LoggingMagics(shell=_ip)
1276 with TemporaryDirectory() as td:
1277 with TemporaryDirectory() as td:
1277 try:
1278 try:
1278 with tt.AssertNotPrints(re.compile("Activating.*")):
1279 with tt.AssertNotPrints(re.compile("Activating.*")):
1279 lm.logstart('-q {}'.format(
1280 lm.logstart('-q {}'.format(
1280 os.path.join(td, "quiet_from_arg.log")))
1281 os.path.join(td, "quiet_from_arg.log")))
1281 finally:
1282 finally:
1282 _ip.logger.logstop()
1283 _ip.logger.logstop()
1283
1284
1284 def test_logging_magic_quiet_from_config():
1285 def test_logging_magic_quiet_from_config():
1285 _ip.config.LoggingMagics.quiet = True
1286 _ip.config.LoggingMagics.quiet = True
1286 lm = logging.LoggingMagics(shell=_ip)
1287 lm = logging.LoggingMagics(shell=_ip)
1287 with TemporaryDirectory() as td:
1288 with TemporaryDirectory() as td:
1288 try:
1289 try:
1289 with tt.AssertNotPrints(re.compile("Activating.*")):
1290 with tt.AssertNotPrints(re.compile("Activating.*")):
1290 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1291 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1291 finally:
1292 finally:
1292 _ip.logger.logstop()
1293 _ip.logger.logstop()
1293
1294
1294
1295
1295 def test_logging_magic_not_quiet():
1296 def test_logging_magic_not_quiet():
1296 _ip.config.LoggingMagics.quiet = False
1297 _ip.config.LoggingMagics.quiet = False
1297 lm = logging.LoggingMagics(shell=_ip)
1298 lm = logging.LoggingMagics(shell=_ip)
1298 with TemporaryDirectory() as td:
1299 with TemporaryDirectory() as td:
1299 try:
1300 try:
1300 with tt.AssertPrints(re.compile("Activating.*")):
1301 with tt.AssertPrints(re.compile("Activating.*")):
1301 lm.logstart(os.path.join(td, "not_quiet.log"))
1302 lm.logstart(os.path.join(td, "not_quiet.log"))
1302 finally:
1303 finally:
1303 _ip.logger.logstop()
1304 _ip.logger.logstop()
1304
1305
1305
1306
1306 def test_time_no_var_expand():
1307 def test_time_no_var_expand():
1307 _ip.user_ns['a'] = 5
1308 _ip.user_ns['a'] = 5
1308 _ip.user_ns['b'] = []
1309 _ip.user_ns['b'] = []
1309 _ip.magic('time b.append("{a}")')
1310 _ip.magic('time b.append("{a}")')
1310 assert _ip.user_ns['b'] == ['{a}']
1311 assert _ip.user_ns['b'] == ['{a}']
1311
1312
1312
1313
1313 # this is slow, put at the end for local testing.
1314 # this is slow, put at the end for local testing.
1314 def test_timeit_arguments():
1315 def test_timeit_arguments():
1315 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1316 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1316 if sys.version_info < (3,7):
1317 if sys.version_info < (3,7):
1317 _ip.magic("timeit -n1 -r1 ('#')")
1318 _ip.magic("timeit -n1 -r1 ('#')")
1318 else:
1319 else:
1319 # 3.7 optimize no-op statement like above out, and complain there is
1320 # 3.7 optimize no-op statement like above out, and complain there is
1320 # nothing in the for loop.
1321 # nothing in the for loop.
1321 _ip.magic("timeit -n1 -r1 a=('#')")
1322 _ip.magic("timeit -n1 -r1 a=('#')")
1322
1323
1323
1324
1324 TEST_MODULE = """
1325 TEST_MODULE = """
1325 print('Loaded my_tmp')
1326 print('Loaded my_tmp')
1326 if __name__ == "__main__":
1327 if __name__ == "__main__":
1327 print('I just ran a script')
1328 print('I just ran a script')
1328 """
1329 """
1329
1330
1330
1331
1331 def test_run_module_from_import_hook():
1332 def test_run_module_from_import_hook():
1332 "Test that a module can be loaded via an import hook"
1333 "Test that a module can be loaded via an import hook"
1333 with TemporaryDirectory() as tmpdir:
1334 with TemporaryDirectory() as tmpdir:
1334 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1335 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1335 Path(fullpath).write_text(TEST_MODULE)
1336 Path(fullpath).write_text(TEST_MODULE)
1336
1337
1337 import importlib.abc
1338 import importlib.abc
1338 import importlib.util
1339 import importlib.util
1339
1340
1340 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1341 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1341 def find_spec(self, fullname, path, target=None):
1342 def find_spec(self, fullname, path, target=None):
1342 if fullname == "my_tmp":
1343 if fullname == "my_tmp":
1343 return importlib.util.spec_from_loader(fullname, self)
1344 return importlib.util.spec_from_loader(fullname, self)
1344
1345
1345 def get_filename(self, fullname):
1346 def get_filename(self, fullname):
1346 if fullname != "my_tmp":
1347 if fullname != "my_tmp":
1347 raise ImportError(f"unexpected module name '{fullname}'")
1348 raise ImportError(f"unexpected module name '{fullname}'")
1348 return fullpath
1349 return fullpath
1349
1350
1350 def get_data(self, path):
1351 def get_data(self, path):
1351 if not Path(path).samefile(fullpath):
1352 if not Path(path).samefile(fullpath):
1352 raise OSError(f"expected path '{fullpath}', got '{path}'")
1353 raise OSError(f"expected path '{fullpath}', got '{path}'")
1353 return Path(fullpath).read_text()
1354 return Path(fullpath).read_text()
1354
1355
1355 sys.meta_path.insert(0, MyTempImporter())
1356 sys.meta_path.insert(0, MyTempImporter())
1356
1357
1357 with capture_output() as captured:
1358 with capture_output() as captured:
1358 _ip.magic("run -m my_tmp")
1359 _ip.magic("run -m my_tmp")
1359 _ip.run_cell("import my_tmp")
1360 _ip.run_cell("import my_tmp")
1360
1361
1361 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1362 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1362 assert output == captured.stdout
1363 assert output == captured.stdout
1363
1364
1364 sys.meta_path.pop(0)
1365 sys.meta_path.pop(0)
@@ -1,599 +1,601 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 from os.path import join as pjoin
21 from os.path import join as pjoin
22 import random
22 import random
23 import string
23 import string
24 import sys
24 import sys
25 import textwrap
25 import textwrap
26 import unittest
26 import unittest
27 from unittest.mock import patch
27 from unittest.mock import patch
28
28
29 import pytest
29 import pytest
30
30
31 from IPython.testing import decorators as dec
31 from IPython.testing import decorators as dec
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils.io import capture_output
33 from IPython.utils.io import capture_output
34 from IPython.utils.tempdir import TemporaryDirectory
34 from IPython.utils.tempdir import TemporaryDirectory
35 from IPython.core import debugger
35 from IPython.core import debugger
36
36
37 def doctest_refbug():
37 def doctest_refbug():
38 """Very nasty problem with references held by multiple runs of a script.
38 """Very nasty problem with references held by multiple runs of a script.
39 See: https://github.com/ipython/ipython/issues/141
39 See: https://github.com/ipython/ipython/issues/141
40
40
41 In [1]: _ip.clear_main_mod_cache()
41 In [1]: _ip.clear_main_mod_cache()
42 # random
42 # random
43
43
44 In [2]: %run refbug
44 In [2]: %run refbug
45
45
46 In [3]: call_f()
46 In [3]: call_f()
47 lowercased: hello
47 lowercased: hello
48
48
49 In [4]: %run refbug
49 In [4]: %run refbug
50
50
51 In [5]: call_f()
51 In [5]: call_f()
52 lowercased: hello
52 lowercased: hello
53 lowercased: hello
53 lowercased: hello
54 """
54 """
55
55
56
56
57 def doctest_run_builtins():
57 def doctest_run_builtins():
58 r"""Check that %run doesn't damage __builtins__.
58 r"""Check that %run doesn't damage __builtins__.
59
59
60 In [1]: import tempfile
60 In [1]: import tempfile
61
61
62 In [2]: bid1 = id(__builtins__)
62 In [2]: bid1 = id(__builtins__)
63
63
64 In [3]: fname = tempfile.mkstemp('.py')[1]
64 In [3]: fname = tempfile.mkstemp('.py')[1]
65
65
66 In [3]: f = open(fname,'w')
66 In [3]: f = open(fname,'w')
67
67
68 In [4]: dummy= f.write('pass\n')
68 In [4]: dummy= f.write('pass\n')
69
69
70 In [5]: f.flush()
70 In [5]: f.flush()
71
71
72 In [6]: t1 = type(__builtins__)
72 In [6]: t1 = type(__builtins__)
73
73
74 In [7]: %run $fname
74 In [7]: %run $fname
75
75
76 In [7]: f.close()
76 In [7]: f.close()
77
77
78 In [8]: bid2 = id(__builtins__)
78 In [8]: bid2 = id(__builtins__)
79
79
80 In [9]: t2 = type(__builtins__)
80 In [9]: t2 = type(__builtins__)
81
81
82 In [10]: t1 == t2
82 In [10]: t1 == t2
83 Out[10]: True
83 Out[10]: True
84
84
85 In [10]: bid1 == bid2
85 In [10]: bid1 == bid2
86 Out[10]: True
86 Out[10]: True
87
87
88 In [12]: try:
88 In [12]: try:
89 ....: os.unlink(fname)
89 ....: os.unlink(fname)
90 ....: except:
90 ....: except:
91 ....: pass
91 ....: pass
92 ....:
92 ....:
93 """
93 """
94
94
95
95
96 def doctest_run_option_parser():
96 def doctest_run_option_parser():
97 r"""Test option parser in %run.
97 r"""Test option parser in %run.
98
98
99 In [1]: %run print_argv.py
99 In [1]: %run print_argv.py
100 []
100 []
101
101
102 In [2]: %run print_argv.py print*.py
102 In [2]: %run print_argv.py print*.py
103 ['print_argv.py']
103 ['print_argv.py']
104
104
105 In [3]: %run -G print_argv.py print*.py
105 In [3]: %run -G print_argv.py print*.py
106 ['print*.py']
106 ['print*.py']
107
107
108 """
108 """
109
109
110
110
111 @dec.skip_win32
111 @dec.skip_win32
112 def doctest_run_option_parser_for_posix():
112 def doctest_run_option_parser_for_posix():
113 r"""Test option parser in %run (Linux/OSX specific).
113 r"""Test option parser in %run (Linux/OSX specific).
114
114
115 You need double quote to escape glob in POSIX systems:
115 You need double quote to escape glob in POSIX systems:
116
116
117 In [1]: %run print_argv.py print\\*.py
117 In [1]: %run print_argv.py print\\*.py
118 ['print*.py']
118 ['print*.py']
119
119
120 You can't use quote to escape glob in POSIX systems:
120 You can't use quote to escape glob in POSIX systems:
121
121
122 In [2]: %run print_argv.py 'print*.py'
122 In [2]: %run print_argv.py 'print*.py'
123 ['print_argv.py']
123 ['print_argv.py']
124
124
125 """
125 """
126
126
127
127
128 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
128 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
129
129
130
130
131 @dec.skip_if_not_win32
131 @dec.skip_if_not_win32
132 def doctest_run_option_parser_for_windows():
132 def doctest_run_option_parser_for_windows():
133 r"""Test option parser in %run (Windows specific).
133 r"""Test option parser in %run (Windows specific).
134
134
135 In Windows, you can't escape ``*` `by backslash:
135 In Windows, you can't escape ``*` `by backslash:
136
136
137 In [1]: %run print_argv.py print\\*.py
137 In [1]: %run print_argv.py print\\*.py
138 ['print\\\\*.py']
138 ['print\\\\*.py']
139
139
140 You can use quote to escape glob:
140 You can use quote to escape glob:
141
141
142 In [2]: %run print_argv.py 'print*.py'
142 In [2]: %run print_argv.py 'print*.py'
143 ["'print*.py'"]
143 ["'print*.py'"]
144
144
145 """
145 """
146
146
147
147
148 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
148 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
149
149
150
150
151 def doctest_reset_del():
151 def doctest_reset_del():
152 """Test that resetting doesn't cause errors in __del__ methods.
152 """Test that resetting doesn't cause errors in __del__ methods.
153
153
154 In [2]: class A(object):
154 In [2]: class A(object):
155 ...: def __del__(self):
155 ...: def __del__(self):
156 ...: print(str("Hi"))
156 ...: print(str("Hi"))
157 ...:
157 ...:
158
158
159 In [3]: a = A()
159 In [3]: a = A()
160
160
161 In [4]: get_ipython().reset()
161 In [4]: get_ipython().reset()
162 Hi
162 Hi
163
163
164 In [5]: 1+1
164 In [5]: 1+1
165 Out[5]: 2
165 Out[5]: 2
166 """
166 """
167
167
168 # For some tests, it will be handy to organize them in a class with a common
168 # For some tests, it will be handy to organize them in a class with a common
169 # setup that makes a temp file
169 # setup that makes a temp file
170
170
171 class TestMagicRunPass(tt.TempFileMixin):
171 class TestMagicRunPass(tt.TempFileMixin):
172
172
173 def setUp(self):
173 def setUp(self):
174 content = "a = [1,2,3]\nb = 1"
174 content = "a = [1,2,3]\nb = 1"
175 self.mktmp(content)
175 self.mktmp(content)
176
176
177 def run_tmpfile(self):
177 def run_tmpfile(self):
178 _ip = get_ipython()
178 _ip = get_ipython()
179 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
179 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
180 # See below and ticket https://bugs.launchpad.net/bugs/366353
180 # See below and ticket https://bugs.launchpad.net/bugs/366353
181 _ip.magic('run %s' % self.fname)
181 _ip.magic('run %s' % self.fname)
182
182
183 def run_tmpfile_p(self):
183 def run_tmpfile_p(self):
184 _ip = get_ipython()
184 _ip = get_ipython()
185 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
185 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
186 # See below and ticket https://bugs.launchpad.net/bugs/366353
186 # See below and ticket https://bugs.launchpad.net/bugs/366353
187 _ip.magic('run -p %s' % self.fname)
187 _ip.magic('run -p %s' % self.fname)
188
188
189 def test_builtins_id(self):
189 def test_builtins_id(self):
190 """Check that %run doesn't damage __builtins__ """
190 """Check that %run doesn't damage __builtins__ """
191 _ip = get_ipython()
191 _ip = get_ipython()
192 # Test that the id of __builtins__ is not modified by %run
192 # Test that the id of __builtins__ is not modified by %run
193 bid1 = id(_ip.user_ns['__builtins__'])
193 bid1 = id(_ip.user_ns['__builtins__'])
194 self.run_tmpfile()
194 self.run_tmpfile()
195 bid2 = id(_ip.user_ns['__builtins__'])
195 bid2 = id(_ip.user_ns['__builtins__'])
196 assert bid1 == bid2
196 assert bid1 == bid2
197
197
198 def test_builtins_type(self):
198 def test_builtins_type(self):
199 """Check that the type of __builtins__ doesn't change with %run.
199 """Check that the type of __builtins__ doesn't change with %run.
200
200
201 However, the above could pass if __builtins__ was already modified to
201 However, the above could pass if __builtins__ was already modified to
202 be a dict (it should be a module) by a previous use of %run. So we
202 be a dict (it should be a module) by a previous use of %run. So we
203 also check explicitly that it really is a module:
203 also check explicitly that it really is a module:
204 """
204 """
205 _ip = get_ipython()
205 _ip = get_ipython()
206 self.run_tmpfile()
206 self.run_tmpfile()
207 assert type(_ip.user_ns["__builtins__"]) == type(sys)
207 assert type(_ip.user_ns["__builtins__"]) == type(sys)
208
208
209 def test_run_profile(self):
209 def test_run_profile(self):
210 """Test that the option -p, which invokes the profiler, do not
210 """Test that the option -p, which invokes the profiler, do not
211 crash by invoking execfile"""
211 crash by invoking execfile"""
212 self.run_tmpfile_p()
212 self.run_tmpfile_p()
213
213
214 def test_run_debug_twice(self):
214 def test_run_debug_twice(self):
215 # https://github.com/ipython/ipython/issues/10028
215 # https://github.com/ipython/ipython/issues/10028
216 _ip = get_ipython()
216 _ip = get_ipython()
217 with tt.fake_input(['c']):
217 with tt.fake_input(['c']):
218 _ip.magic('run -d %s' % self.fname)
218 _ip.magic('run -d %s' % self.fname)
219 with tt.fake_input(['c']):
219 with tt.fake_input(['c']):
220 _ip.magic('run -d %s' % self.fname)
220 _ip.magic('run -d %s' % self.fname)
221
221
222 def test_run_debug_twice_with_breakpoint(self):
222 def test_run_debug_twice_with_breakpoint(self):
223 """Make a valid python temp file."""
223 """Make a valid python temp file."""
224 _ip = get_ipython()
224 _ip = get_ipython()
225 with tt.fake_input(['b 2', 'c', 'c']):
225 with tt.fake_input(['b 2', 'c', 'c']):
226 _ip.magic('run -d %s' % self.fname)
226 _ip.magic('run -d %s' % self.fname)
227
227
228 with tt.fake_input(['c']):
228 with tt.fake_input(['c']):
229 with tt.AssertNotPrints('KeyError'):
229 with tt.AssertNotPrints('KeyError'):
230 _ip.magic('run -d %s' % self.fname)
230 _ip.magic('run -d %s' % self.fname)
231
231
232
232
233 class TestMagicRunSimple(tt.TempFileMixin):
233 class TestMagicRunSimple(tt.TempFileMixin):
234
234
235 def test_simpledef(self):
235 def test_simpledef(self):
236 """Test that simple class definitions work."""
236 """Test that simple class definitions work."""
237 src = ("class foo: pass\n"
237 src = ("class foo: pass\n"
238 "def f(): return foo()")
238 "def f(): return foo()")
239 self.mktmp(src)
239 self.mktmp(src)
240 _ip.magic("run %s" % self.fname)
240 _ip.magic("run %s" % self.fname)
241 _ip.run_cell("t = isinstance(f(), foo)")
241 _ip.run_cell("t = isinstance(f(), foo)")
242 assert _ip.user_ns["t"] is True
242 assert _ip.user_ns["t"] is True
243
243
244 def test_obj_del(self):
244 def test_obj_del(self):
245 """Test that object's __del__ methods are called on exit."""
245 """Test that object's __del__ methods are called on exit."""
246 src = ("class A(object):\n"
246 src = ("class A(object):\n"
247 " def __del__(self):\n"
247 " def __del__(self):\n"
248 " print('object A deleted')\n"
248 " print('object A deleted')\n"
249 "a = A()\n")
249 "a = A()\n")
250 self.mktmp(src)
250 self.mktmp(src)
251 err = None
251 err = None
252 tt.ipexec_validate(self.fname, 'object A deleted', err)
252 tt.ipexec_validate(self.fname, 'object A deleted', err)
253
253
254 def test_aggressive_namespace_cleanup(self):
254 def test_aggressive_namespace_cleanup(self):
255 """Test that namespace cleanup is not too aggressive GH-238
255 """Test that namespace cleanup is not too aggressive GH-238
256
256
257 Returning from another run magic deletes the namespace"""
257 Returning from another run magic deletes the namespace"""
258 # see ticket https://github.com/ipython/ipython/issues/238
258 # see ticket https://github.com/ipython/ipython/issues/238
259
259
260 with tt.TempFileMixin() as empty:
260 with tt.TempFileMixin() as empty:
261 empty.mktmp("")
261 empty.mktmp("")
262 # On Windows, the filename will have \users in it, so we need to use the
262 # On Windows, the filename will have \users in it, so we need to use the
263 # repr so that the \u becomes \\u.
263 # repr so that the \u becomes \\u.
264 src = (
264 src = (
265 "ip = get_ipython()\n"
265 "ip = get_ipython()\n"
266 "for i in range(5):\n"
266 "for i in range(5):\n"
267 " try:\n"
267 " try:\n"
268 " ip.magic(%r)\n"
268 " ip.magic(%r)\n"
269 " except NameError as e:\n"
269 " except NameError as e:\n"
270 " print(i)\n"
270 " print(i)\n"
271 " break\n" % ("run " + empty.fname)
271 " break\n" % ("run " + empty.fname)
272 )
272 )
273 self.mktmp(src)
273 self.mktmp(src)
274 _ip.magic("run %s" % self.fname)
274 _ip.magic("run %s" % self.fname)
275 _ip.run_cell("ip == get_ipython()")
275 _ip.run_cell("ip == get_ipython()")
276 assert _ip.user_ns["i"] == 4
276 assert _ip.user_ns["i"] == 4
277
277
278 def test_run_second(self):
278 def test_run_second(self):
279 """Test that running a second file doesn't clobber the first, gh-3547"""
279 """Test that running a second file doesn't clobber the first, gh-3547"""
280 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
280 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
281
281
282 with tt.TempFileMixin() as empty:
282 with tt.TempFileMixin() as empty:
283 empty.mktmp("")
283 empty.mktmp("")
284
284
285 _ip.magic("run %s" % self.fname)
285 _ip.magic("run %s" % self.fname)
286 _ip.magic("run %s" % empty.fname)
286 _ip.magic("run %s" % empty.fname)
287 assert _ip.user_ns["afunc"]() == 1
287 assert _ip.user_ns["afunc"]() == 1
288
288
289 @dec.skip_win32
289 @dec.skip_win32
290 def test_tclass(self):
290 def test_tclass(self):
291 mydir = os.path.dirname(__file__)
291 mydir = os.path.dirname(__file__)
292 tc = os.path.join(mydir, 'tclass')
292 tc = os.path.join(mydir, 'tclass')
293 src = ("%%run '%s' C-first\n"
293 src = ("%%run '%s' C-first\n"
294 "%%run '%s' C-second\n"
294 "%%run '%s' C-second\n"
295 "%%run '%s' C-third\n") % (tc, tc, tc)
295 "%%run '%s' C-third\n") % (tc, tc, tc)
296 self.mktmp(src, '.ipy')
296 self.mktmp(src, '.ipy')
297 out = """\
297 out = """\
298 ARGV 1-: ['C-first']
298 ARGV 1-: ['C-first']
299 ARGV 1-: ['C-second']
299 ARGV 1-: ['C-second']
300 tclass.py: deleting object: C-first
300 tclass.py: deleting object: C-first
301 ARGV 1-: ['C-third']
301 ARGV 1-: ['C-third']
302 tclass.py: deleting object: C-second
302 tclass.py: deleting object: C-second
303 tclass.py: deleting object: C-third
303 tclass.py: deleting object: C-third
304 """
304 """
305 err = None
305 err = None
306 tt.ipexec_validate(self.fname, out, err)
306 tt.ipexec_validate(self.fname, out, err)
307
307
308 def test_run_i_after_reset(self):
308 def test_run_i_after_reset(self):
309 """Check that %run -i still works after %reset (gh-693)"""
309 """Check that %run -i still works after %reset (gh-693)"""
310 src = "yy = zz\n"
310 src = "yy = zz\n"
311 self.mktmp(src)
311 self.mktmp(src)
312 _ip.run_cell("zz = 23")
312 _ip.run_cell("zz = 23")
313 try:
313 try:
314 _ip.magic("run -i %s" % self.fname)
314 _ip.magic("run -i %s" % self.fname)
315 assert _ip.user_ns["yy"] == 23
315 assert _ip.user_ns["yy"] == 23
316 finally:
316 finally:
317 _ip.magic('reset -f')
317 _ip.magic('reset -f')
318
318
319 _ip.run_cell("zz = 23")
319 _ip.run_cell("zz = 23")
320 try:
320 try:
321 _ip.magic("run -i %s" % self.fname)
321 _ip.magic("run -i %s" % self.fname)
322 assert _ip.user_ns["yy"] == 23
322 assert _ip.user_ns["yy"] == 23
323 finally:
323 finally:
324 _ip.magic('reset -f')
324 _ip.magic('reset -f')
325
325
326 def test_unicode(self):
326 def test_unicode(self):
327 """Check that files in odd encodings are accepted."""
327 """Check that files in odd encodings are accepted."""
328 mydir = os.path.dirname(__file__)
328 mydir = os.path.dirname(__file__)
329 na = os.path.join(mydir, 'nonascii.py')
329 na = os.path.join(mydir, 'nonascii.py')
330 _ip.magic('run "%s"' % na)
330 _ip.magic('run "%s"' % na)
331 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
331 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
332
332
333 def test_run_py_file_attribute(self):
333 def test_run_py_file_attribute(self):
334 """Test handling of `__file__` attribute in `%run <file>.py`."""
334 """Test handling of `__file__` attribute in `%run <file>.py`."""
335 src = "t = __file__\n"
335 src = "t = __file__\n"
336 self.mktmp(src)
336 self.mktmp(src)
337 _missing = object()
337 _missing = object()
338 file1 = _ip.user_ns.get('__file__', _missing)
338 file1 = _ip.user_ns.get('__file__', _missing)
339 _ip.magic('run %s' % self.fname)
339 _ip.magic('run %s' % self.fname)
340 file2 = _ip.user_ns.get('__file__', _missing)
340 file2 = _ip.user_ns.get('__file__', _missing)
341
341
342 # Check that __file__ was equal to the filename in the script's
342 # Check that __file__ was equal to the filename in the script's
343 # namespace.
343 # namespace.
344 assert _ip.user_ns["t"] == self.fname
344 assert _ip.user_ns["t"] == self.fname
345
345
346 # Check that __file__ was not leaked back into user_ns.
346 # Check that __file__ was not leaked back into user_ns.
347 assert file1 == file2
347 assert file1 == file2
348
348
349 def test_run_ipy_file_attribute(self):
349 def test_run_ipy_file_attribute(self):
350 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
350 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
351 src = "t = __file__\n"
351 src = "t = __file__\n"
352 self.mktmp(src, ext='.ipy')
352 self.mktmp(src, ext='.ipy')
353 _missing = object()
353 _missing = object()
354 file1 = _ip.user_ns.get('__file__', _missing)
354 file1 = _ip.user_ns.get('__file__', _missing)
355 _ip.magic('run %s' % self.fname)
355 _ip.magic('run %s' % self.fname)
356 file2 = _ip.user_ns.get('__file__', _missing)
356 file2 = _ip.user_ns.get('__file__', _missing)
357
357
358 # Check that __file__ was equal to the filename in the script's
358 # Check that __file__ was equal to the filename in the script's
359 # namespace.
359 # namespace.
360 assert _ip.user_ns["t"] == self.fname
360 assert _ip.user_ns["t"] == self.fname
361
361
362 # Check that __file__ was not leaked back into user_ns.
362 # Check that __file__ was not leaked back into user_ns.
363 assert file1 == file2
363 assert file1 == file2
364
364
365 def test_run_formatting(self):
365 def test_run_formatting(self):
366 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
366 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
367 src = "pass"
367 src = "pass"
368 self.mktmp(src)
368 self.mktmp(src)
369 _ip.magic('run -t -N 1 %s' % self.fname)
369 _ip.magic('run -t -N 1 %s' % self.fname)
370 _ip.magic('run -t -N 10 %s' % self.fname)
370 _ip.magic('run -t -N 10 %s' % self.fname)
371
371
372 def test_ignore_sys_exit(self):
372 def test_ignore_sys_exit(self):
373 """Test the -e option to ignore sys.exit()"""
373 """Test the -e option to ignore sys.exit()"""
374 src = "import sys; sys.exit(1)"
374 src = "import sys; sys.exit(1)"
375 self.mktmp(src)
375 self.mktmp(src)
376 with tt.AssertPrints('SystemExit'):
376 with tt.AssertPrints('SystemExit'):
377 _ip.magic('run %s' % self.fname)
377 _ip.magic('run %s' % self.fname)
378
378
379 with tt.AssertNotPrints('SystemExit'):
379 with tt.AssertNotPrints('SystemExit'):
380 _ip.magic('run -e %s' % self.fname)
380 _ip.magic('run -e %s' % self.fname)
381
381
382 def test_run_nb(self):
382 def test_run_nb(self):
383 """Test %run notebook.ipynb"""
383 """Test %run notebook.ipynb"""
384 pytest.importorskip("nbformat")
384 from nbformat import v4, writes
385 from nbformat import v4, writes
385 nb = v4.new_notebook(
386 nb = v4.new_notebook(
386 cells=[
387 cells=[
387 v4.new_markdown_cell("The Ultimate Question of Everything"),
388 v4.new_markdown_cell("The Ultimate Question of Everything"),
388 v4.new_code_cell("answer=42")
389 v4.new_code_cell("answer=42")
389 ]
390 ]
390 )
391 )
391 src = writes(nb, version=4)
392 src = writes(nb, version=4)
392 self.mktmp(src, ext='.ipynb')
393 self.mktmp(src, ext='.ipynb')
393
394
394 _ip.magic("run %s" % self.fname)
395 _ip.magic("run %s" % self.fname)
395
396
396 assert _ip.user_ns["answer"] == 42
397 assert _ip.user_ns["answer"] == 42
397
398
398 def test_run_nb_error(self):
399 def test_run_nb_error(self):
399 """Test %run notebook.ipynb error"""
400 """Test %run notebook.ipynb error"""
401 pytest.importorskip("nbformat")
400 from nbformat import v4, writes
402 from nbformat import v4, writes
401 # %run when a file name isn't provided
403 # %run when a file name isn't provided
402 pytest.raises(Exception, _ip.magic, "run")
404 pytest.raises(Exception, _ip.magic, "run")
403
405
404 # %run when a file doesn't exist
406 # %run when a file doesn't exist
405 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
407 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
406
408
407 # %run on a notebook with an error
409 # %run on a notebook with an error
408 nb = v4.new_notebook(
410 nb = v4.new_notebook(
409 cells=[
411 cells=[
410 v4.new_code_cell("0/0")
412 v4.new_code_cell("0/0")
411 ]
413 ]
412 )
414 )
413 src = writes(nb, version=4)
415 src = writes(nb, version=4)
414 self.mktmp(src, ext='.ipynb')
416 self.mktmp(src, ext='.ipynb')
415 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
417 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
416
418
417 def test_file_options(self):
419 def test_file_options(self):
418 src = ('import sys\n'
420 src = ('import sys\n'
419 'a = " ".join(sys.argv[1:])\n')
421 'a = " ".join(sys.argv[1:])\n')
420 self.mktmp(src)
422 self.mktmp(src)
421 test_opts = "-x 3 --verbose"
423 test_opts = "-x 3 --verbose"
422 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
424 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
423 assert _ip.user_ns["a"] == test_opts
425 assert _ip.user_ns["a"] == test_opts
424
426
425
427
426 class TestMagicRunWithPackage(unittest.TestCase):
428 class TestMagicRunWithPackage(unittest.TestCase):
427
429
428 def writefile(self, name, content):
430 def writefile(self, name, content):
429 path = os.path.join(self.tempdir.name, name)
431 path = os.path.join(self.tempdir.name, name)
430 d = os.path.dirname(path)
432 d = os.path.dirname(path)
431 if not os.path.isdir(d):
433 if not os.path.isdir(d):
432 os.makedirs(d)
434 os.makedirs(d)
433 with open(path, 'w') as f:
435 with open(path, 'w') as f:
434 f.write(textwrap.dedent(content))
436 f.write(textwrap.dedent(content))
435
437
436 def setUp(self):
438 def setUp(self):
437 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
439 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
438 """Temporary (probably) valid python package name."""
440 """Temporary (probably) valid python package name."""
439
441
440 self.value = int(random.random() * 10000)
442 self.value = int(random.random() * 10000)
441
443
442 self.tempdir = TemporaryDirectory()
444 self.tempdir = TemporaryDirectory()
443 self.__orig_cwd = os.getcwd()
445 self.__orig_cwd = os.getcwd()
444 sys.path.insert(0, self.tempdir.name)
446 sys.path.insert(0, self.tempdir.name)
445
447
446 self.writefile(os.path.join(package, '__init__.py'), '')
448 self.writefile(os.path.join(package, '__init__.py'), '')
447 self.writefile(os.path.join(package, 'sub.py'), """
449 self.writefile(os.path.join(package, 'sub.py'), """
448 x = {0!r}
450 x = {0!r}
449 """.format(self.value))
451 """.format(self.value))
450 self.writefile(os.path.join(package, 'relative.py'), """
452 self.writefile(os.path.join(package, 'relative.py'), """
451 from .sub import x
453 from .sub import x
452 """)
454 """)
453 self.writefile(os.path.join(package, 'absolute.py'), """
455 self.writefile(os.path.join(package, 'absolute.py'), """
454 from {0}.sub import x
456 from {0}.sub import x
455 """.format(package))
457 """.format(package))
456 self.writefile(os.path.join(package, 'args.py'), """
458 self.writefile(os.path.join(package, 'args.py'), """
457 import sys
459 import sys
458 a = " ".join(sys.argv[1:])
460 a = " ".join(sys.argv[1:])
459 """.format(package))
461 """.format(package))
460
462
461 def tearDown(self):
463 def tearDown(self):
462 os.chdir(self.__orig_cwd)
464 os.chdir(self.__orig_cwd)
463 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
465 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
464 self.tempdir.cleanup()
466 self.tempdir.cleanup()
465
467
466 def check_run_submodule(self, submodule, opts=''):
468 def check_run_submodule(self, submodule, opts=''):
467 _ip.user_ns.pop('x', None)
469 _ip.user_ns.pop('x', None)
468 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
470 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
469 self.assertEqual(_ip.user_ns['x'], self.value,
471 self.assertEqual(_ip.user_ns['x'], self.value,
470 'Variable `x` is not loaded from module `{0}`.'
472 'Variable `x` is not loaded from module `{0}`.'
471 .format(submodule))
473 .format(submodule))
472
474
473 def test_run_submodule_with_absolute_import(self):
475 def test_run_submodule_with_absolute_import(self):
474 self.check_run_submodule('absolute')
476 self.check_run_submodule('absolute')
475
477
476 def test_run_submodule_with_relative_import(self):
478 def test_run_submodule_with_relative_import(self):
477 """Run submodule that has a relative import statement (#2727)."""
479 """Run submodule that has a relative import statement (#2727)."""
478 self.check_run_submodule('relative')
480 self.check_run_submodule('relative')
479
481
480 def test_prun_submodule_with_absolute_import(self):
482 def test_prun_submodule_with_absolute_import(self):
481 self.check_run_submodule('absolute', '-p')
483 self.check_run_submodule('absolute', '-p')
482
484
483 def test_prun_submodule_with_relative_import(self):
485 def test_prun_submodule_with_relative_import(self):
484 self.check_run_submodule('relative', '-p')
486 self.check_run_submodule('relative', '-p')
485
487
486 def with_fake_debugger(func):
488 def with_fake_debugger(func):
487 @functools.wraps(func)
489 @functools.wraps(func)
488 def wrapper(*args, **kwds):
490 def wrapper(*args, **kwds):
489 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
491 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
490 return func(*args, **kwds)
492 return func(*args, **kwds)
491 return wrapper
493 return wrapper
492
494
493 @with_fake_debugger
495 @with_fake_debugger
494 def test_debug_run_submodule_with_absolute_import(self):
496 def test_debug_run_submodule_with_absolute_import(self):
495 self.check_run_submodule('absolute', '-d')
497 self.check_run_submodule('absolute', '-d')
496
498
497 @with_fake_debugger
499 @with_fake_debugger
498 def test_debug_run_submodule_with_relative_import(self):
500 def test_debug_run_submodule_with_relative_import(self):
499 self.check_run_submodule('relative', '-d')
501 self.check_run_submodule('relative', '-d')
500
502
501 def test_module_options(self):
503 def test_module_options(self):
502 _ip.user_ns.pop("a", None)
504 _ip.user_ns.pop("a", None)
503 test_opts = "-x abc -m test"
505 test_opts = "-x abc -m test"
504 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
506 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
505 assert _ip.user_ns["a"] == test_opts
507 assert _ip.user_ns["a"] == test_opts
506
508
507 def test_module_options_with_separator(self):
509 def test_module_options_with_separator(self):
508 _ip.user_ns.pop("a", None)
510 _ip.user_ns.pop("a", None)
509 test_opts = "-x abc -m test"
511 test_opts = "-x abc -m test"
510 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
512 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
511 assert _ip.user_ns["a"] == test_opts
513 assert _ip.user_ns["a"] == test_opts
512
514
513
515
514 def test_run__name__():
516 def test_run__name__():
515 with TemporaryDirectory() as td:
517 with TemporaryDirectory() as td:
516 path = pjoin(td, 'foo.py')
518 path = pjoin(td, 'foo.py')
517 with open(path, 'w') as f:
519 with open(path, 'w') as f:
518 f.write("q = __name__")
520 f.write("q = __name__")
519
521
520 _ip.user_ns.pop("q", None)
522 _ip.user_ns.pop("q", None)
521 _ip.magic("run {}".format(path))
523 _ip.magic("run {}".format(path))
522 assert _ip.user_ns.pop("q") == "__main__"
524 assert _ip.user_ns.pop("q") == "__main__"
523
525
524 _ip.magic("run -n {}".format(path))
526 _ip.magic("run -n {}".format(path))
525 assert _ip.user_ns.pop("q") == "foo"
527 assert _ip.user_ns.pop("q") == "foo"
526
528
527 try:
529 try:
528 _ip.magic("run -i -n {}".format(path))
530 _ip.magic("run -i -n {}".format(path))
529 assert _ip.user_ns.pop("q") == "foo"
531 assert _ip.user_ns.pop("q") == "foo"
530 finally:
532 finally:
531 _ip.magic('reset -f')
533 _ip.magic('reset -f')
532
534
533
535
534 def test_run_tb():
536 def test_run_tb():
535 """Test traceback offset in %run"""
537 """Test traceback offset in %run"""
536 with TemporaryDirectory() as td:
538 with TemporaryDirectory() as td:
537 path = pjoin(td, 'foo.py')
539 path = pjoin(td, 'foo.py')
538 with open(path, 'w') as f:
540 with open(path, 'w') as f:
539 f.write('\n'.join([
541 f.write('\n'.join([
540 "def foo():",
542 "def foo():",
541 " return bar()",
543 " return bar()",
542 "def bar():",
544 "def bar():",
543 " raise RuntimeError('hello!')",
545 " raise RuntimeError('hello!')",
544 "foo()",
546 "foo()",
545 ]))
547 ]))
546 with capture_output() as io:
548 with capture_output() as io:
547 _ip.magic('run {}'.format(path))
549 _ip.magic('run {}'.format(path))
548 out = io.stdout
550 out = io.stdout
549 assert "execfile" not in out
551 assert "execfile" not in out
550 assert "RuntimeError" in out
552 assert "RuntimeError" in out
551 assert out.count("---->") == 3
553 assert out.count("---->") == 3
552 del ip.user_ns['bar']
554 del ip.user_ns['bar']
553 del ip.user_ns['foo']
555 del ip.user_ns['foo']
554
556
555
557
556 def test_multiprocessing_run():
558 def test_multiprocessing_run():
557 """Set we can run mutiprocesgin without messing up up main namespace
559 """Set we can run mutiprocesgin without messing up up main namespace
558
560
559 Note that import `nose.tools as nt` mdify the value s
561 Note that import `nose.tools as nt` mdify the value s
560 sys.module['__mp_main__'] so we need to temporarily set it to None to test
562 sys.module['__mp_main__'] so we need to temporarily set it to None to test
561 the issue.
563 the issue.
562 """
564 """
563 with TemporaryDirectory() as td:
565 with TemporaryDirectory() as td:
564 mpm = sys.modules.get('__mp_main__')
566 mpm = sys.modules.get('__mp_main__')
565 sys.modules['__mp_main__'] = None
567 sys.modules['__mp_main__'] = None
566 try:
568 try:
567 path = pjoin(td, 'test.py')
569 path = pjoin(td, 'test.py')
568 with open(path, 'w') as f:
570 with open(path, 'w') as f:
569 f.write("import multiprocessing\nprint('hoy')")
571 f.write("import multiprocessing\nprint('hoy')")
570 with capture_output() as io:
572 with capture_output() as io:
571 _ip.run_line_magic('run', path)
573 _ip.run_line_magic('run', path)
572 _ip.run_cell("i_m_undefined")
574 _ip.run_cell("i_m_undefined")
573 out = io.stdout
575 out = io.stdout
574 assert "hoy" in out
576 assert "hoy" in out
575 assert "AttributeError" not in out
577 assert "AttributeError" not in out
576 assert "NameError" in out
578 assert "NameError" in out
577 assert out.count("---->") == 1
579 assert out.count("---->") == 1
578 except:
580 except:
579 raise
581 raise
580 finally:
582 finally:
581 sys.modules['__mp_main__'] = mpm
583 sys.modules['__mp_main__'] = mpm
582
584
583
585
584 def test_script_tb():
586 def test_script_tb():
585 """Test traceback offset in `ipython script.py`"""
587 """Test traceback offset in `ipython script.py`"""
586 with TemporaryDirectory() as td:
588 with TemporaryDirectory() as td:
587 path = pjoin(td, 'foo.py')
589 path = pjoin(td, 'foo.py')
588 with open(path, 'w') as f:
590 with open(path, 'w') as f:
589 f.write('\n'.join([
591 f.write('\n'.join([
590 "def foo():",
592 "def foo():",
591 " return bar()",
593 " return bar()",
592 "def bar():",
594 "def bar():",
593 " raise RuntimeError('hello!')",
595 " raise RuntimeError('hello!')",
594 "foo()",
596 "foo()",
595 ]))
597 ]))
596 out, err = tt.ipexec(path)
598 out, err = tt.ipexec(path)
597 assert "execfile" not in out
599 assert "execfile" not in out
598 assert "RuntimeError" in out
600 assert "RuntimeError" in out
599 assert out.count("---->") == 3
601 assert out.count("---->") == 3
@@ -1,674 +1,677 b''
1 """Various display related classes.
1 """Various display related classes.
2
2
3 Authors : MinRK, gregcaporaso, dannystaple
3 Authors : MinRK, gregcaporaso, dannystaple
4 """
4 """
5 from html import escape as html_escape
5 from html import escape as html_escape
6 from os.path import exists, isfile, splitext, abspath, join, isdir
6 from os.path import exists, isfile, splitext, abspath, join, isdir
7 from os import walk, sep, fsdecode
7 from os import walk, sep, fsdecode
8
8
9 from IPython.core.display import DisplayObject, TextDisplayObject
9 from IPython.core.display import DisplayObject, TextDisplayObject
10
10
11 from typing import Tuple, Iterable
11 from typing import Tuple, Iterable
12
12
13 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
13 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
14 'FileLink', 'FileLinks', 'Code']
14 'FileLink', 'FileLinks', 'Code']
15
15
16
16
17 class Audio(DisplayObject):
17 class Audio(DisplayObject):
18 """Create an audio object.
18 """Create an audio object.
19
19
20 When this object is returned by an input cell or passed to the
20 When this object is returned by an input cell or passed to the
21 display function, it will result in Audio controls being displayed
21 display function, it will result in Audio controls being displayed
22 in the frontend (only works in the notebook).
22 in the frontend (only works in the notebook).
23
23
24 Parameters
24 Parameters
25 ----------
25 ----------
26 data : numpy array, list, unicode, str or bytes
26 data : numpy array, list, unicode, str or bytes
27 Can be one of
27 Can be one of
28
28
29 * Numpy 1d array containing the desired waveform (mono)
29 * Numpy 1d array containing the desired waveform (mono)
30 * Numpy 2d array containing waveforms for each channel.
30 * Numpy 2d array containing waveforms for each channel.
31 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
31 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
32 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
32 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
33 * List of float or integer representing the waveform (mono)
33 * List of float or integer representing the waveform (mono)
34 * String containing the filename
34 * String containing the filename
35 * Bytestring containing raw PCM data or
35 * Bytestring containing raw PCM data or
36 * URL pointing to a file on the web.
36 * URL pointing to a file on the web.
37
37
38 If the array option is used, the waveform will be normalized.
38 If the array option is used, the waveform will be normalized.
39
39
40 If a filename or url is used, the format support will be browser
40 If a filename or url is used, the format support will be browser
41 dependent.
41 dependent.
42 url : unicode
42 url : unicode
43 A URL to download the data from.
43 A URL to download the data from.
44 filename : unicode
44 filename : unicode
45 Path to a local file to load the data from.
45 Path to a local file to load the data from.
46 embed : boolean
46 embed : boolean
47 Should the audio data be embedded using a data URI (True) or should
47 Should the audio data be embedded using a data URI (True) or should
48 the original source be referenced. Set this to True if you want the
48 the original source be referenced. Set this to True if you want the
49 audio to playable later with no internet connection in the notebook.
49 audio to playable later with no internet connection in the notebook.
50
50
51 Default is `True`, unless the keyword argument `url` is set, then
51 Default is `True`, unless the keyword argument `url` is set, then
52 default value is `False`.
52 default value is `False`.
53 rate : integer
53 rate : integer
54 The sampling rate of the raw data.
54 The sampling rate of the raw data.
55 Only required when data parameter is being used as an array
55 Only required when data parameter is being used as an array
56 autoplay : bool
56 autoplay : bool
57 Set to True if the audio should immediately start playing.
57 Set to True if the audio should immediately start playing.
58 Default is `False`.
58 Default is `False`.
59 normalize : bool
59 normalize : bool
60 Whether audio should be normalized (rescaled) to the maximum possible
60 Whether audio should be normalized (rescaled) to the maximum possible
61 range. Default is `True`. When set to `False`, `data` must be between
61 range. Default is `True`. When set to `False`, `data` must be between
62 -1 and 1 (inclusive), otherwise an error is raised.
62 -1 and 1 (inclusive), otherwise an error is raised.
63 Applies only when `data` is a list or array of samples; other types of
63 Applies only when `data` is a list or array of samples; other types of
64 audio are never normalized.
64 audio are never normalized.
65
65
66 Examples
66 Examples
67 --------
67 --------
68
68
69 >>> import pytest
70 >>> np = pytest.importorskip("numpy")
71
69 Generate a sound
72 Generate a sound
70
73
71 >>> import numpy as np
74 >>> import numpy as np
72 >>> framerate = 44100
75 >>> framerate = 44100
73 >>> t = np.linspace(0,5,framerate*5)
76 >>> t = np.linspace(0,5,framerate*5)
74 >>> data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
77 >>> data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
75 >>> Audio(data, rate=framerate)
78 >>> Audio(data, rate=framerate)
76 <IPython.lib.display.Audio object>
79 <IPython.lib.display.Audio object>
77
80
78 Can also do stereo or more channels
81 Can also do stereo or more channels
79
82
80 >>> dataleft = np.sin(2*np.pi*220*t)
83 >>> dataleft = np.sin(2*np.pi*220*t)
81 >>> dataright = np.sin(2*np.pi*224*t)
84 >>> dataright = np.sin(2*np.pi*224*t)
82 >>> Audio([dataleft, dataright], rate=framerate)
85 >>> Audio([dataleft, dataright], rate=framerate)
83 <IPython.lib.display.Audio object>
86 <IPython.lib.display.Audio object>
84
87
85 From URL:
88 From URL:
86
89
87 >>> Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # doctest: +SKIP
90 >>> Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # doctest: +SKIP
88 >>> Audio(url="http://www.w3schools.com/html/horse.ogg") # doctest: +SKIP
91 >>> Audio(url="http://www.w3schools.com/html/horse.ogg") # doctest: +SKIP
89
92
90 From a File:
93 From a File:
91
94
92 >>> Audio('/path/to/sound.wav') # doctest: +SKIP
95 >>> Audio('/path/to/sound.wav') # doctest: +SKIP
93 >>> Audio(filename='/path/to/sound.ogg') # doctest: +SKIP
96 >>> Audio(filename='/path/to/sound.ogg') # doctest: +SKIP
94
97
95 From Bytes:
98 From Bytes:
96
99
97 >>> Audio(b'RAW_WAV_DATA..') # doctest: +SKIP
100 >>> Audio(b'RAW_WAV_DATA..') # doctest: +SKIP
98 >>> Audio(data=b'RAW_WAV_DATA..') # doctest: +SKIP
101 >>> Audio(data=b'RAW_WAV_DATA..') # doctest: +SKIP
99
102
100 See Also
103 See Also
101 --------
104 --------
102 ipywidgets.Audio
105 ipywidgets.Audio
103
106
104 AUdio widget with more more flexibility and options.
107 AUdio widget with more more flexibility and options.
105
108
106 """
109 """
107 _read_flags = 'rb'
110 _read_flags = 'rb'
108
111
109 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
112 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
110 element_id=None):
113 element_id=None):
111 if filename is None and url is None and data is None:
114 if filename is None and url is None and data is None:
112 raise ValueError("No audio data found. Expecting filename, url, or data.")
115 raise ValueError("No audio data found. Expecting filename, url, or data.")
113 if embed is False and url is None:
116 if embed is False and url is None:
114 raise ValueError("No url found. Expecting url when embed=False")
117 raise ValueError("No url found. Expecting url when embed=False")
115
118
116 if url is not None and embed is not True:
119 if url is not None and embed is not True:
117 self.embed = False
120 self.embed = False
118 else:
121 else:
119 self.embed = True
122 self.embed = True
120 self.autoplay = autoplay
123 self.autoplay = autoplay
121 self.element_id = element_id
124 self.element_id = element_id
122 super(Audio, self).__init__(data=data, url=url, filename=filename)
125 super(Audio, self).__init__(data=data, url=url, filename=filename)
123
126
124 if self.data is not None and not isinstance(self.data, bytes):
127 if self.data is not None and not isinstance(self.data, bytes):
125 if rate is None:
128 if rate is None:
126 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
129 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
127 self.data = Audio._make_wav(data, rate, normalize)
130 self.data = Audio._make_wav(data, rate, normalize)
128
131
129 def reload(self):
132 def reload(self):
130 """Reload the raw data from file or URL."""
133 """Reload the raw data from file or URL."""
131 import mimetypes
134 import mimetypes
132 if self.embed:
135 if self.embed:
133 super(Audio, self).reload()
136 super(Audio, self).reload()
134
137
135 if self.filename is not None:
138 if self.filename is not None:
136 self.mimetype = mimetypes.guess_type(self.filename)[0]
139 self.mimetype = mimetypes.guess_type(self.filename)[0]
137 elif self.url is not None:
140 elif self.url is not None:
138 self.mimetype = mimetypes.guess_type(self.url)[0]
141 self.mimetype = mimetypes.guess_type(self.url)[0]
139 else:
142 else:
140 self.mimetype = "audio/wav"
143 self.mimetype = "audio/wav"
141
144
142 @staticmethod
145 @staticmethod
143 def _make_wav(data, rate, normalize):
146 def _make_wav(data, rate, normalize):
144 """ Transform a numpy array to a PCM bytestring """
147 """ Transform a numpy array to a PCM bytestring """
145 from io import BytesIO
148 from io import BytesIO
146 import wave
149 import wave
147
150
148 try:
151 try:
149 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
152 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
150 except ImportError:
153 except ImportError:
151 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
154 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
152
155
153 fp = BytesIO()
156 fp = BytesIO()
154 waveobj = wave.open(fp,mode='wb')
157 waveobj = wave.open(fp,mode='wb')
155 waveobj.setnchannels(nchan)
158 waveobj.setnchannels(nchan)
156 waveobj.setframerate(rate)
159 waveobj.setframerate(rate)
157 waveobj.setsampwidth(2)
160 waveobj.setsampwidth(2)
158 waveobj.setcomptype('NONE','NONE')
161 waveobj.setcomptype('NONE','NONE')
159 waveobj.writeframes(scaled)
162 waveobj.writeframes(scaled)
160 val = fp.getvalue()
163 val = fp.getvalue()
161 waveobj.close()
164 waveobj.close()
162
165
163 return val
166 return val
164
167
165 @staticmethod
168 @staticmethod
166 def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]:
169 def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]:
167 import numpy as np
170 import numpy as np
168
171
169 data = np.array(data, dtype=float)
172 data = np.array(data, dtype=float)
170 if len(data.shape) == 1:
173 if len(data.shape) == 1:
171 nchan = 1
174 nchan = 1
172 elif len(data.shape) == 2:
175 elif len(data.shape) == 2:
173 # In wave files,channels are interleaved. E.g.,
176 # In wave files,channels are interleaved. E.g.,
174 # "L1R1L2R2..." for stereo. See
177 # "L1R1L2R2..." for stereo. See
175 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
178 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
176 # for channel ordering
179 # for channel ordering
177 nchan = data.shape[0]
180 nchan = data.shape[0]
178 data = data.T.ravel()
181 data = data.T.ravel()
179 else:
182 else:
180 raise ValueError('Array audio input must be a 1D or 2D array')
183 raise ValueError('Array audio input must be a 1D or 2D array')
181
184
182 max_abs_value = np.max(np.abs(data))
185 max_abs_value = np.max(np.abs(data))
183 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
186 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
184 scaled = data / normalization_factor * 32767
187 scaled = data / normalization_factor * 32767
185 return scaled.astype("<h").tobytes(), nchan
188 return scaled.astype("<h").tobytes(), nchan
186
189
187 @staticmethod
190 @staticmethod
188 def _validate_and_normalize_without_numpy(data, normalize):
191 def _validate_and_normalize_without_numpy(data, normalize):
189 import array
192 import array
190 import sys
193 import sys
191
194
192 data = array.array('f', data)
195 data = array.array('f', data)
193
196
194 try:
197 try:
195 max_abs_value = float(max([abs(x) for x in data]))
198 max_abs_value = float(max([abs(x) for x in data]))
196 except TypeError as e:
199 except TypeError as e:
197 raise TypeError('Only lists of mono audio are '
200 raise TypeError('Only lists of mono audio are '
198 'supported if numpy is not installed') from e
201 'supported if numpy is not installed') from e
199
202
200 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
203 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
201 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
204 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
202 if sys.byteorder == 'big':
205 if sys.byteorder == 'big':
203 scaled.byteswap()
206 scaled.byteswap()
204 nchan = 1
207 nchan = 1
205 return scaled.tobytes(), nchan
208 return scaled.tobytes(), nchan
206
209
207 @staticmethod
210 @staticmethod
208 def _get_normalization_factor(max_abs_value, normalize):
211 def _get_normalization_factor(max_abs_value, normalize):
209 if not normalize and max_abs_value > 1:
212 if not normalize and max_abs_value > 1:
210 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
213 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
211 return max_abs_value if normalize else 1
214 return max_abs_value if normalize else 1
212
215
213 def _data_and_metadata(self):
216 def _data_and_metadata(self):
214 """shortcut for returning metadata with url information, if defined"""
217 """shortcut for returning metadata with url information, if defined"""
215 md = {}
218 md = {}
216 if self.url:
219 if self.url:
217 md['url'] = self.url
220 md['url'] = self.url
218 if md:
221 if md:
219 return self.data, md
222 return self.data, md
220 else:
223 else:
221 return self.data
224 return self.data
222
225
223 def _repr_html_(self):
226 def _repr_html_(self):
224 src = """
227 src = """
225 <audio {element_id} controls="controls" {autoplay}>
228 <audio {element_id} controls="controls" {autoplay}>
226 <source src="{src}" type="{type}" />
229 <source src="{src}" type="{type}" />
227 Your browser does not support the audio element.
230 Your browser does not support the audio element.
228 </audio>
231 </audio>
229 """
232 """
230 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
233 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
231 element_id=self.element_id_attr())
234 element_id=self.element_id_attr())
232
235
233 def src_attr(self):
236 def src_attr(self):
234 import base64
237 import base64
235 if self.embed and (self.data is not None):
238 if self.embed and (self.data is not None):
236 data = base64=base64.b64encode(self.data).decode('ascii')
239 data = base64=base64.b64encode(self.data).decode('ascii')
237 return """data:{type};base64,{base64}""".format(type=self.mimetype,
240 return """data:{type};base64,{base64}""".format(type=self.mimetype,
238 base64=data)
241 base64=data)
239 elif self.url is not None:
242 elif self.url is not None:
240 return self.url
243 return self.url
241 else:
244 else:
242 return ""
245 return ""
243
246
244 def autoplay_attr(self):
247 def autoplay_attr(self):
245 if(self.autoplay):
248 if(self.autoplay):
246 return 'autoplay="autoplay"'
249 return 'autoplay="autoplay"'
247 else:
250 else:
248 return ''
251 return ''
249
252
250 def element_id_attr(self):
253 def element_id_attr(self):
251 if (self.element_id):
254 if (self.element_id):
252 return 'id="{element_id}"'.format(element_id=self.element_id)
255 return 'id="{element_id}"'.format(element_id=self.element_id)
253 else:
256 else:
254 return ''
257 return ''
255
258
256 class IFrame(object):
259 class IFrame(object):
257 """
260 """
258 Generic class to embed an iframe in an IPython notebook
261 Generic class to embed an iframe in an IPython notebook
259 """
262 """
260
263
261 iframe = """
264 iframe = """
262 <iframe
265 <iframe
263 width="{width}"
266 width="{width}"
264 height="{height}"
267 height="{height}"
265 src="{src}{params}"
268 src="{src}{params}"
266 frameborder="0"
269 frameborder="0"
267 allowfullscreen
270 allowfullscreen
268 {extras}
271 {extras}
269 ></iframe>
272 ></iframe>
270 """
273 """
271
274
272 def __init__(self, src, width, height, extras: Iterable[str] = None, **kwargs):
275 def __init__(self, src, width, height, extras: Iterable[str] = None, **kwargs):
273 if extras is None:
276 if extras is None:
274 extras = []
277 extras = []
275
278
276 self.src = src
279 self.src = src
277 self.width = width
280 self.width = width
278 self.height = height
281 self.height = height
279 self.extras = extras
282 self.extras = extras
280 self.params = kwargs
283 self.params = kwargs
281
284
282 def _repr_html_(self):
285 def _repr_html_(self):
283 """return the embed iframe"""
286 """return the embed iframe"""
284 if self.params:
287 if self.params:
285 from urllib.parse import urlencode
288 from urllib.parse import urlencode
286 params = "?" + urlencode(self.params)
289 params = "?" + urlencode(self.params)
287 else:
290 else:
288 params = ""
291 params = ""
289 return self.iframe.format(
292 return self.iframe.format(
290 src=self.src,
293 src=self.src,
291 width=self.width,
294 width=self.width,
292 height=self.height,
295 height=self.height,
293 params=params,
296 params=params,
294 extras=" ".join(self.extras),
297 extras=" ".join(self.extras),
295 )
298 )
296
299
297
300
298 class YouTubeVideo(IFrame):
301 class YouTubeVideo(IFrame):
299 """Class for embedding a YouTube Video in an IPython session, based on its video id.
302 """Class for embedding a YouTube Video in an IPython session, based on its video id.
300
303
301 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
304 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
302 do::
305 do::
303
306
304 vid = YouTubeVideo("foo")
307 vid = YouTubeVideo("foo")
305 display(vid)
308 display(vid)
306
309
307 To start from 30 seconds::
310 To start from 30 seconds::
308
311
309 vid = YouTubeVideo("abc", start=30)
312 vid = YouTubeVideo("abc", start=30)
310 display(vid)
313 display(vid)
311
314
312 To calculate seconds from time as hours, minutes, seconds use
315 To calculate seconds from time as hours, minutes, seconds use
313 :class:`datetime.timedelta`::
316 :class:`datetime.timedelta`::
314
317
315 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
318 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
316
319
317 Other parameters can be provided as documented at
320 Other parameters can be provided as documented at
318 https://developers.google.com/youtube/player_parameters#Parameters
321 https://developers.google.com/youtube/player_parameters#Parameters
319
322
320 When converting the notebook using nbconvert, a jpeg representation of the video
323 When converting the notebook using nbconvert, a jpeg representation of the video
321 will be inserted in the document.
324 will be inserted in the document.
322 """
325 """
323
326
324 def __init__(self, id, width=400, height=300, allow_autoplay=False, **kwargs):
327 def __init__(self, id, width=400, height=300, allow_autoplay=False, **kwargs):
325 self.id=id
328 self.id=id
326 src = "https://www.youtube.com/embed/{0}".format(id)
329 src = "https://www.youtube.com/embed/{0}".format(id)
327 if allow_autoplay:
330 if allow_autoplay:
328 extras = list(kwargs.get("extras", [])) + ['allow="autoplay"']
331 extras = list(kwargs.get("extras", [])) + ['allow="autoplay"']
329 kwargs.update(autoplay=1, extras=extras)
332 kwargs.update(autoplay=1, extras=extras)
330 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
333 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
331
334
332 def _repr_jpeg_(self):
335 def _repr_jpeg_(self):
333 # Deferred import
336 # Deferred import
334 from urllib.request import urlopen
337 from urllib.request import urlopen
335
338
336 try:
339 try:
337 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
340 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
338 except IOError:
341 except IOError:
339 return None
342 return None
340
343
341 class VimeoVideo(IFrame):
344 class VimeoVideo(IFrame):
342 """
345 """
343 Class for embedding a Vimeo video in an IPython session, based on its video id.
346 Class for embedding a Vimeo video in an IPython session, based on its video id.
344 """
347 """
345
348
346 def __init__(self, id, width=400, height=300, **kwargs):
349 def __init__(self, id, width=400, height=300, **kwargs):
347 src="https://player.vimeo.com/video/{0}".format(id)
350 src="https://player.vimeo.com/video/{0}".format(id)
348 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
351 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
349
352
350 class ScribdDocument(IFrame):
353 class ScribdDocument(IFrame):
351 """
354 """
352 Class for embedding a Scribd document in an IPython session
355 Class for embedding a Scribd document in an IPython session
353
356
354 Use the start_page params to specify a starting point in the document
357 Use the start_page params to specify a starting point in the document
355 Use the view_mode params to specify display type one off scroll | slideshow | book
358 Use the view_mode params to specify display type one off scroll | slideshow | book
356
359
357 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
360 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
358
361
359 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
362 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
360 """
363 """
361
364
362 def __init__(self, id, width=400, height=300, **kwargs):
365 def __init__(self, id, width=400, height=300, **kwargs):
363 src="https://www.scribd.com/embeds/{0}/content".format(id)
366 src="https://www.scribd.com/embeds/{0}/content".format(id)
364 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
367 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
365
368
366 class FileLink(object):
369 class FileLink(object):
367 """Class for embedding a local file link in an IPython session, based on path
370 """Class for embedding a local file link in an IPython session, based on path
368
371
369 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
372 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
370
373
371 you would do::
374 you would do::
372
375
373 local_file = FileLink("my/data.txt")
376 local_file = FileLink("my/data.txt")
374 display(local_file)
377 display(local_file)
375
378
376 or in the HTML notebook, just::
379 or in the HTML notebook, just::
377
380
378 FileLink("my/data.txt")
381 FileLink("my/data.txt")
379 """
382 """
380
383
381 html_link_str = "<a href='%s' target='_blank'>%s</a>"
384 html_link_str = "<a href='%s' target='_blank'>%s</a>"
382
385
383 def __init__(self,
386 def __init__(self,
384 path,
387 path,
385 url_prefix='',
388 url_prefix='',
386 result_html_prefix='',
389 result_html_prefix='',
387 result_html_suffix='<br>'):
390 result_html_suffix='<br>'):
388 """
391 """
389 Parameters
392 Parameters
390 ----------
393 ----------
391 path : str
394 path : str
392 path to the file or directory that should be formatted
395 path to the file or directory that should be formatted
393 url_prefix : str
396 url_prefix : str
394 prefix to be prepended to all files to form a working link [default:
397 prefix to be prepended to all files to form a working link [default:
395 '']
398 '']
396 result_html_prefix : str
399 result_html_prefix : str
397 text to append to beginning to link [default: '']
400 text to append to beginning to link [default: '']
398 result_html_suffix : str
401 result_html_suffix : str
399 text to append at the end of link [default: '<br>']
402 text to append at the end of link [default: '<br>']
400 """
403 """
401 if isdir(path):
404 if isdir(path):
402 raise ValueError("Cannot display a directory using FileLink. "
405 raise ValueError("Cannot display a directory using FileLink. "
403 "Use FileLinks to display '%s'." % path)
406 "Use FileLinks to display '%s'." % path)
404 self.path = fsdecode(path)
407 self.path = fsdecode(path)
405 self.url_prefix = url_prefix
408 self.url_prefix = url_prefix
406 self.result_html_prefix = result_html_prefix
409 self.result_html_prefix = result_html_prefix
407 self.result_html_suffix = result_html_suffix
410 self.result_html_suffix = result_html_suffix
408
411
409 def _format_path(self):
412 def _format_path(self):
410 fp = ''.join([self.url_prefix, html_escape(self.path)])
413 fp = ''.join([self.url_prefix, html_escape(self.path)])
411 return ''.join([self.result_html_prefix,
414 return ''.join([self.result_html_prefix,
412 self.html_link_str % \
415 self.html_link_str % \
413 (fp, html_escape(self.path, quote=False)),
416 (fp, html_escape(self.path, quote=False)),
414 self.result_html_suffix])
417 self.result_html_suffix])
415
418
416 def _repr_html_(self):
419 def _repr_html_(self):
417 """return html link to file
420 """return html link to file
418 """
421 """
419 if not exists(self.path):
422 if not exists(self.path):
420 return ("Path (<tt>%s</tt>) doesn't exist. "
423 return ("Path (<tt>%s</tt>) doesn't exist. "
421 "It may still be in the process of "
424 "It may still be in the process of "
422 "being generated, or you may have the "
425 "being generated, or you may have the "
423 "incorrect path." % self.path)
426 "incorrect path." % self.path)
424
427
425 return self._format_path()
428 return self._format_path()
426
429
427 def __repr__(self):
430 def __repr__(self):
428 """return absolute path to file
431 """return absolute path to file
429 """
432 """
430 return abspath(self.path)
433 return abspath(self.path)
431
434
432 class FileLinks(FileLink):
435 class FileLinks(FileLink):
433 """Class for embedding local file links in an IPython session, based on path
436 """Class for embedding local file links in an IPython session, based on path
434
437
435 e.g. to embed links to files that were generated in the IPython notebook
438 e.g. to embed links to files that were generated in the IPython notebook
436 under ``my/data``, you would do::
439 under ``my/data``, you would do::
437
440
438 local_files = FileLinks("my/data")
441 local_files = FileLinks("my/data")
439 display(local_files)
442 display(local_files)
440
443
441 or in the HTML notebook, just::
444 or in the HTML notebook, just::
442
445
443 FileLinks("my/data")
446 FileLinks("my/data")
444 """
447 """
445 def __init__(self,
448 def __init__(self,
446 path,
449 path,
447 url_prefix='',
450 url_prefix='',
448 included_suffixes=None,
451 included_suffixes=None,
449 result_html_prefix='',
452 result_html_prefix='',
450 result_html_suffix='<br>',
453 result_html_suffix='<br>',
451 notebook_display_formatter=None,
454 notebook_display_formatter=None,
452 terminal_display_formatter=None,
455 terminal_display_formatter=None,
453 recursive=True):
456 recursive=True):
454 """
457 """
455 See :class:`FileLink` for the ``path``, ``url_prefix``,
458 See :class:`FileLink` for the ``path``, ``url_prefix``,
456 ``result_html_prefix`` and ``result_html_suffix`` parameters.
459 ``result_html_prefix`` and ``result_html_suffix`` parameters.
457
460
458 included_suffixes : list
461 included_suffixes : list
459 Filename suffixes to include when formatting output [default: include
462 Filename suffixes to include when formatting output [default: include
460 all files]
463 all files]
461
464
462 notebook_display_formatter : function
465 notebook_display_formatter : function
463 Used to format links for display in the notebook. See discussion of
466 Used to format links for display in the notebook. See discussion of
464 formatter functions below.
467 formatter functions below.
465
468
466 terminal_display_formatter : function
469 terminal_display_formatter : function
467 Used to format links for display in the terminal. See discussion of
470 Used to format links for display in the terminal. See discussion of
468 formatter functions below.
471 formatter functions below.
469
472
470 Formatter functions must be of the form::
473 Formatter functions must be of the form::
471
474
472 f(dirname, fnames, included_suffixes)
475 f(dirname, fnames, included_suffixes)
473
476
474 dirname : str
477 dirname : str
475 The name of a directory
478 The name of a directory
476 fnames : list
479 fnames : list
477 The files in that directory
480 The files in that directory
478 included_suffixes : list
481 included_suffixes : list
479 The file suffixes that should be included in the output (passing None
482 The file suffixes that should be included in the output (passing None
480 meansto include all suffixes in the output in the built-in formatters)
483 meansto include all suffixes in the output in the built-in formatters)
481 recursive : boolean
484 recursive : boolean
482 Whether to recurse into subdirectories. Default is True.
485 Whether to recurse into subdirectories. Default is True.
483
486
484 The function should return a list of lines that will be printed in the
487 The function should return a list of lines that will be printed in the
485 notebook (if passing notebook_display_formatter) or the terminal (if
488 notebook (if passing notebook_display_formatter) or the terminal (if
486 passing terminal_display_formatter). This function is iterated over for
489 passing terminal_display_formatter). This function is iterated over for
487 each directory in self.path. Default formatters are in place, can be
490 each directory in self.path. Default formatters are in place, can be
488 passed here to support alternative formatting.
491 passed here to support alternative formatting.
489
492
490 """
493 """
491 if isfile(path):
494 if isfile(path):
492 raise ValueError("Cannot display a file using FileLinks. "
495 raise ValueError("Cannot display a file using FileLinks. "
493 "Use FileLink to display '%s'." % path)
496 "Use FileLink to display '%s'." % path)
494 self.included_suffixes = included_suffixes
497 self.included_suffixes = included_suffixes
495 # remove trailing slashes for more consistent output formatting
498 # remove trailing slashes for more consistent output formatting
496 path = path.rstrip('/')
499 path = path.rstrip('/')
497
500
498 self.path = path
501 self.path = path
499 self.url_prefix = url_prefix
502 self.url_prefix = url_prefix
500 self.result_html_prefix = result_html_prefix
503 self.result_html_prefix = result_html_prefix
501 self.result_html_suffix = result_html_suffix
504 self.result_html_suffix = result_html_suffix
502
505
503 self.notebook_display_formatter = \
506 self.notebook_display_formatter = \
504 notebook_display_formatter or self._get_notebook_display_formatter()
507 notebook_display_formatter or self._get_notebook_display_formatter()
505 self.terminal_display_formatter = \
508 self.terminal_display_formatter = \
506 terminal_display_formatter or self._get_terminal_display_formatter()
509 terminal_display_formatter or self._get_terminal_display_formatter()
507
510
508 self.recursive = recursive
511 self.recursive = recursive
509
512
510 def _get_display_formatter(self,
513 def _get_display_formatter(self,
511 dirname_output_format,
514 dirname_output_format,
512 fname_output_format,
515 fname_output_format,
513 fp_format,
516 fp_format,
514 fp_cleaner=None):
517 fp_cleaner=None):
515 """ generate built-in formatter function
518 """ generate built-in formatter function
516
519
517 this is used to define both the notebook and terminal built-in
520 this is used to define both the notebook and terminal built-in
518 formatters as they only differ by some wrapper text for each entry
521 formatters as they only differ by some wrapper text for each entry
519
522
520 dirname_output_format: string to use for formatting directory
523 dirname_output_format: string to use for formatting directory
521 names, dirname will be substituted for a single "%s" which
524 names, dirname will be substituted for a single "%s" which
522 must appear in this string
525 must appear in this string
523 fname_output_format: string to use for formatting file names,
526 fname_output_format: string to use for formatting file names,
524 if a single "%s" appears in the string, fname will be substituted
527 if a single "%s" appears in the string, fname will be substituted
525 if two "%s" appear in the string, the path to fname will be
528 if two "%s" appear in the string, the path to fname will be
526 substituted for the first and fname will be substituted for the
529 substituted for the first and fname will be substituted for the
527 second
530 second
528 fp_format: string to use for formatting filepaths, must contain
531 fp_format: string to use for formatting filepaths, must contain
529 exactly two "%s" and the dirname will be substituted for the first
532 exactly two "%s" and the dirname will be substituted for the first
530 and fname will be substituted for the second
533 and fname will be substituted for the second
531 """
534 """
532 def f(dirname, fnames, included_suffixes=None):
535 def f(dirname, fnames, included_suffixes=None):
533 result = []
536 result = []
534 # begin by figuring out which filenames, if any,
537 # begin by figuring out which filenames, if any,
535 # are going to be displayed
538 # are going to be displayed
536 display_fnames = []
539 display_fnames = []
537 for fname in fnames:
540 for fname in fnames:
538 if (isfile(join(dirname,fname)) and
541 if (isfile(join(dirname,fname)) and
539 (included_suffixes is None or
542 (included_suffixes is None or
540 splitext(fname)[1] in included_suffixes)):
543 splitext(fname)[1] in included_suffixes)):
541 display_fnames.append(fname)
544 display_fnames.append(fname)
542
545
543 if len(display_fnames) == 0:
546 if len(display_fnames) == 0:
544 # if there are no filenames to display, don't print anything
547 # if there are no filenames to display, don't print anything
545 # (not even the directory name)
548 # (not even the directory name)
546 pass
549 pass
547 else:
550 else:
548 # otherwise print the formatted directory name followed by
551 # otherwise print the formatted directory name followed by
549 # the formatted filenames
552 # the formatted filenames
550 dirname_output_line = dirname_output_format % dirname
553 dirname_output_line = dirname_output_format % dirname
551 result.append(dirname_output_line)
554 result.append(dirname_output_line)
552 for fname in display_fnames:
555 for fname in display_fnames:
553 fp = fp_format % (dirname,fname)
556 fp = fp_format % (dirname,fname)
554 if fp_cleaner is not None:
557 if fp_cleaner is not None:
555 fp = fp_cleaner(fp)
558 fp = fp_cleaner(fp)
556 try:
559 try:
557 # output can include both a filepath and a filename...
560 # output can include both a filepath and a filename...
558 fname_output_line = fname_output_format % (fp, fname)
561 fname_output_line = fname_output_format % (fp, fname)
559 except TypeError:
562 except TypeError:
560 # ... or just a single filepath
563 # ... or just a single filepath
561 fname_output_line = fname_output_format % fname
564 fname_output_line = fname_output_format % fname
562 result.append(fname_output_line)
565 result.append(fname_output_line)
563 return result
566 return result
564 return f
567 return f
565
568
566 def _get_notebook_display_formatter(self,
569 def _get_notebook_display_formatter(self,
567 spacer="&nbsp;&nbsp;"):
570 spacer="&nbsp;&nbsp;"):
568 """ generate function to use for notebook formatting
571 """ generate function to use for notebook formatting
569 """
572 """
570 dirname_output_format = \
573 dirname_output_format = \
571 self.result_html_prefix + "%s/" + self.result_html_suffix
574 self.result_html_prefix + "%s/" + self.result_html_suffix
572 fname_output_format = \
575 fname_output_format = \
573 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
576 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
574 fp_format = self.url_prefix + '%s/%s'
577 fp_format = self.url_prefix + '%s/%s'
575 if sep == "\\":
578 if sep == "\\":
576 # Working on a platform where the path separator is "\", so
579 # Working on a platform where the path separator is "\", so
577 # must convert these to "/" for generating a URI
580 # must convert these to "/" for generating a URI
578 def fp_cleaner(fp):
581 def fp_cleaner(fp):
579 # Replace all occurrences of backslash ("\") with a forward
582 # Replace all occurrences of backslash ("\") with a forward
580 # slash ("/") - this is necessary on windows when a path is
583 # slash ("/") - this is necessary on windows when a path is
581 # provided as input, but we must link to a URI
584 # provided as input, but we must link to a URI
582 return fp.replace('\\','/')
585 return fp.replace('\\','/')
583 else:
586 else:
584 fp_cleaner = None
587 fp_cleaner = None
585
588
586 return self._get_display_formatter(dirname_output_format,
589 return self._get_display_formatter(dirname_output_format,
587 fname_output_format,
590 fname_output_format,
588 fp_format,
591 fp_format,
589 fp_cleaner)
592 fp_cleaner)
590
593
591 def _get_terminal_display_formatter(self,
594 def _get_terminal_display_formatter(self,
592 spacer=" "):
595 spacer=" "):
593 """ generate function to use for terminal formatting
596 """ generate function to use for terminal formatting
594 """
597 """
595 dirname_output_format = "%s/"
598 dirname_output_format = "%s/"
596 fname_output_format = spacer + "%s"
599 fname_output_format = spacer + "%s"
597 fp_format = '%s/%s'
600 fp_format = '%s/%s'
598
601
599 return self._get_display_formatter(dirname_output_format,
602 return self._get_display_formatter(dirname_output_format,
600 fname_output_format,
603 fname_output_format,
601 fp_format)
604 fp_format)
602
605
603 def _format_path(self):
606 def _format_path(self):
604 result_lines = []
607 result_lines = []
605 if self.recursive:
608 if self.recursive:
606 walked_dir = list(walk(self.path))
609 walked_dir = list(walk(self.path))
607 else:
610 else:
608 walked_dir = [next(walk(self.path))]
611 walked_dir = [next(walk(self.path))]
609 walked_dir.sort()
612 walked_dir.sort()
610 for dirname, subdirs, fnames in walked_dir:
613 for dirname, subdirs, fnames in walked_dir:
611 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
614 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
612 return '\n'.join(result_lines)
615 return '\n'.join(result_lines)
613
616
614 def __repr__(self):
617 def __repr__(self):
615 """return newline-separated absolute paths
618 """return newline-separated absolute paths
616 """
619 """
617 result_lines = []
620 result_lines = []
618 if self.recursive:
621 if self.recursive:
619 walked_dir = list(walk(self.path))
622 walked_dir = list(walk(self.path))
620 else:
623 else:
621 walked_dir = [next(walk(self.path))]
624 walked_dir = [next(walk(self.path))]
622 walked_dir.sort()
625 walked_dir.sort()
623 for dirname, subdirs, fnames in walked_dir:
626 for dirname, subdirs, fnames in walked_dir:
624 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
627 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
625 return '\n'.join(result_lines)
628 return '\n'.join(result_lines)
626
629
627
630
628 class Code(TextDisplayObject):
631 class Code(TextDisplayObject):
629 """Display syntax-highlighted source code.
632 """Display syntax-highlighted source code.
630
633
631 This uses Pygments to highlight the code for HTML and Latex output.
634 This uses Pygments to highlight the code for HTML and Latex output.
632
635
633 Parameters
636 Parameters
634 ----------
637 ----------
635 data : str
638 data : str
636 The code as a string
639 The code as a string
637 url : str
640 url : str
638 A URL to fetch the code from
641 A URL to fetch the code from
639 filename : str
642 filename : str
640 A local filename to load the code from
643 A local filename to load the code from
641 language : str
644 language : str
642 The short name of a Pygments lexer to use for highlighting.
645 The short name of a Pygments lexer to use for highlighting.
643 If not specified, it will guess the lexer based on the filename
646 If not specified, it will guess the lexer based on the filename
644 or the code. Available lexers: http://pygments.org/docs/lexers/
647 or the code. Available lexers: http://pygments.org/docs/lexers/
645 """
648 """
646 def __init__(self, data=None, url=None, filename=None, language=None):
649 def __init__(self, data=None, url=None, filename=None, language=None):
647 self.language = language
650 self.language = language
648 super().__init__(data=data, url=url, filename=filename)
651 super().__init__(data=data, url=url, filename=filename)
649
652
650 def _get_lexer(self):
653 def _get_lexer(self):
651 if self.language:
654 if self.language:
652 from pygments.lexers import get_lexer_by_name
655 from pygments.lexers import get_lexer_by_name
653 return get_lexer_by_name(self.language)
656 return get_lexer_by_name(self.language)
654 elif self.filename:
657 elif self.filename:
655 from pygments.lexers import get_lexer_for_filename
658 from pygments.lexers import get_lexer_for_filename
656 return get_lexer_for_filename(self.filename)
659 return get_lexer_for_filename(self.filename)
657 else:
660 else:
658 from pygments.lexers import guess_lexer
661 from pygments.lexers import guess_lexer
659 return guess_lexer(self.data)
662 return guess_lexer(self.data)
660
663
661 def __repr__(self):
664 def __repr__(self):
662 return self.data
665 return self.data
663
666
664 def _repr_html_(self):
667 def _repr_html_(self):
665 from pygments import highlight
668 from pygments import highlight
666 from pygments.formatters import HtmlFormatter
669 from pygments.formatters import HtmlFormatter
667 fmt = HtmlFormatter()
670 fmt = HtmlFormatter()
668 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
671 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
669 return style + highlight(self.data, self._get_lexer(), fmt)
672 return style + highlight(self.data, self._get_lexer(), fmt)
670
673
671 def _repr_latex_(self):
674 def _repr_latex_(self):
672 from pygments import highlight
675 from pygments import highlight
673 from pygments.formatters import LatexFormatter
676 from pygments.formatters import LatexFormatter
674 return highlight(self.data, self._get_lexer(), LatexFormatter())
677 return highlight(self.data, self._get_lexer(), LatexFormatter())
@@ -1,28 +1,30 b''
1 """Test help output of various IPython entry points"""
1 """Test help output of various IPython entry points"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import pytest
6 import IPython.testing.tools as tt
7 import IPython.testing.tools as tt
7
8
8
9
9 def test_ipython_help():
10 def test_ipython_help():
10 tt.help_all_output_test()
11 tt.help_all_output_test()
11
12
12 def test_profile_help():
13 def test_profile_help():
13 tt.help_all_output_test("profile")
14 tt.help_all_output_test("profile")
14
15
15 def test_profile_list_help():
16 def test_profile_list_help():
16 tt.help_all_output_test("profile list")
17 tt.help_all_output_test("profile list")
17
18
18 def test_profile_create_help():
19 def test_profile_create_help():
19 tt.help_all_output_test("profile create")
20 tt.help_all_output_test("profile create")
20
21
21 def test_locate_help():
22 def test_locate_help():
22 tt.help_all_output_test("locate")
23 tt.help_all_output_test("locate")
23
24
24 def test_locate_profile_help():
25 def test_locate_profile_help():
25 tt.help_all_output_test("locate profile")
26 tt.help_all_output_test("locate profile")
26
27
27 def test_trust_help():
28 def test_trust_help():
29 pytest.importorskip("nbformat")
28 tt.help_all_output_test("trust")
30 tt.help_all_output_test("trust")
@@ -1,40 +1,40 b''
1 build: false
1 build: false
2 matrix:
2 matrix:
3 fast_finish: true # immediately finish build once one of the jobs fails.
3 fast_finish: true # immediately finish build once one of the jobs fails.
4
4
5 environment:
5 environment:
6 global:
6 global:
7 APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022'
7 APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022'
8 COLUMNS: 120 # Appveyor web viwer window width is 130 chars
8 COLUMNS: 120 # Appveyor web viwer window width is 130 chars
9
9
10 matrix:
10 matrix:
11
11
12 - PYTHON: "C:\\Python310-x64"
12 - PYTHON: "C:\\Python310-x64"
13 PYTHON_VERSION: "3.10.x"
13 PYTHON_VERSION: "3.10.x"
14 PYTHON_ARCH: "64"
14 PYTHON_ARCH: "64"
15
15
16 - PYTHON: "C:\\Python37-x64"
16 - PYTHON: "C:\\Python37-x64"
17 PYTHON_VERSION: "3.7.x"
17 PYTHON_VERSION: "3.7.x"
18 PYTHON_ARCH: "64"
18 PYTHON_ARCH: "64"
19
19
20 - PYTHON: "C:\\Python38"
20 - PYTHON: "C:\\Python38"
21 PYTHON_VERSION: "3.8.x"
21 PYTHON_VERSION: "3.8.x"
22 PYTHON_ARCH: "32"
22 PYTHON_ARCH: "32"
23
23
24 - PYTHON: "C:\\Python38-x64"
24 - PYTHON: "C:\\Python38-x64"
25 PYTHON_VERSION: "3.8.x"
25 PYTHON_VERSION: "3.8.x"
26 PYTHON_ARCH: "64"
26 PYTHON_ARCH: "64"
27
27
28 init:
28 init:
29 - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
29 - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
30
30
31 install:
31 install:
32 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
32 - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
33 - python -m pip install --upgrade setuptools pip
33 - python -m pip install --upgrade setuptools pip
34 - pip install pytest pytest-cov pytest-trio matplotlib pandas
34 - pip install pytest-cov
35 - pip install -e .[test]
35 - pip install -e .[test_extra]
36 test_script:
36 test_script:
37 - pytest --color=yes -ra --cov --cov-report=xml
37 - pytest --color=yes -ra --cov --cov-report=xml
38 on_finish:
38 on_finish:
39 - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
39 - curl -Os https://uploader.codecov.io/latest/windows/codecov.exe
40 - codecov -e PYTHON_VERSION,PYTHON_ARCH
40 - codecov -e PYTHON_VERSION,PYTHON_ARCH
@@ -1,271 +1,280 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import os
20 import os
21 import sys
21 import sys
22 from pathlib import Path
22 from pathlib import Path
23
23
24 # **Python version check**
24 # **Python version check**
25 #
25 #
26 # This check is also made in IPython/__init__, don't forget to update both when
26 # This check is also made in IPython/__init__, don't forget to update both when
27 # changing Python version requirements.
27 # changing Python version requirements.
28 if sys.version_info < (3, 7):
28 if sys.version_info < (3, 7):
29 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
29 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
30 try:
30 try:
31 import pip
31 import pip
32 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
32 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
33 if pip_version < (9, 0, 1) :
33 if pip_version < (9, 0, 1) :
34 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
34 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
35 'pip {} detected.'.format(pip.__version__)
35 'pip {} detected.'.format(pip.__version__)
36 else:
36 else:
37 # pip is new enough - it must be something else
37 # pip is new enough - it must be something else
38 pip_message = ''
38 pip_message = ''
39 except Exception:
39 except Exception:
40 pass
40 pass
41
41
42
42
43 error = """
43 error = """
44 IPython 7.17+ supports Python 3.7 and above, following NEP 29.
44 IPython 7.17+ supports Python 3.7 and above, following NEP 29.
45 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
45 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 Python 3.3 and 3.4 were supported up to IPython 6.x.
46 Python 3.3 and 3.4 were supported up to IPython 6.x.
47 Python 3.5 was supported with IPython 7.0 to 7.9.
47 Python 3.5 was supported with IPython 7.0 to 7.9.
48 Python 3.6 was supported with IPython up to 7.16.
48 Python 3.6 was supported with IPython up to 7.16.
49
49
50 See IPython `README.rst` file for more information:
50 See IPython `README.rst` file for more information:
51
51
52 https://github.com/ipython/ipython/blob/master/README.rst
52 https://github.com/ipython/ipython/blob/master/README.rst
53
53
54 Python {py} detected.
54 Python {py} detected.
55 {pip}
55 {pip}
56 """.format(py=sys.version_info, pip=pip_message )
56 """.format(py=sys.version_info, pip=pip_message )
57
57
58 print(error, file=sys.stderr)
58 print(error, file=sys.stderr)
59 sys.exit(1)
59 sys.exit(1)
60
60
61 # At least we're on the python version we need, move on.
61 # At least we're on the python version we need, move on.
62
62
63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
64 # update it when the contents of directories change.
64 # update it when the contents of directories change.
65 if Path("MANIFEST").exists():
65 if Path("MANIFEST").exists():
66 Path("MANIFEST").unlink()
66 Path("MANIFEST").unlink()
67
67
68 from distutils.core import setup
68 from distutils.core import setup
69
69
70 # Our own imports
70 # Our own imports
71 from setupbase import target_update
71 from setupbase import target_update
72
72
73 from setupbase import (
73 from setupbase import (
74 setup_args,
74 setup_args,
75 find_packages,
75 find_packages,
76 find_package_data,
76 find_package_data,
77 check_package_data_first,
77 check_package_data_first,
78 find_entry_points,
78 find_entry_points,
79 build_scripts_entrypt,
79 build_scripts_entrypt,
80 find_data_files,
80 find_data_files,
81 git_prebuild,
81 git_prebuild,
82 install_symlinked,
82 install_symlinked,
83 install_lib_symlink,
83 install_lib_symlink,
84 install_scripts_for_symlink,
84 install_scripts_for_symlink,
85 unsymlink,
85 unsymlink,
86 )
86 )
87
87
88 #-------------------------------------------------------------------------------
88 #-------------------------------------------------------------------------------
89 # Handle OS specific things
89 # Handle OS specific things
90 #-------------------------------------------------------------------------------
90 #-------------------------------------------------------------------------------
91
91
92 if os.name in ('nt','dos'):
92 if os.name in ('nt','dos'):
93 os_name = 'windows'
93 os_name = 'windows'
94 else:
94 else:
95 os_name = os.name
95 os_name = os.name
96
96
97 # Under Windows, 'sdist' has not been supported. Now that the docs build with
97 # Under Windows, 'sdist' has not been supported. Now that the docs build with
98 # Sphinx it might work, but let's not turn it on until someone confirms that it
98 # Sphinx it might work, but let's not turn it on until someone confirms that it
99 # actually works.
99 # actually works.
100 if os_name == 'windows' and 'sdist' in sys.argv:
100 if os_name == 'windows' and 'sdist' in sys.argv:
101 print('The sdist command is not available under Windows. Exiting.')
101 print('The sdist command is not available under Windows. Exiting.')
102 sys.exit(1)
102 sys.exit(1)
103
103
104
104
105 #-------------------------------------------------------------------------------
105 #-------------------------------------------------------------------------------
106 # Things related to the IPython documentation
106 # Things related to the IPython documentation
107 #-------------------------------------------------------------------------------
107 #-------------------------------------------------------------------------------
108
108
109 # update the manuals when building a source dist
109 # update the manuals when building a source dist
110 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
110 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
111
111
112 # List of things to be updated. Each entry is a triplet of args for
112 # List of things to be updated. Each entry is a triplet of args for
113 # target_update()
113 # target_update()
114 to_update = [
114 to_update = [
115 ('docs/man/ipython.1.gz',
115 ('docs/man/ipython.1.gz',
116 ['docs/man/ipython.1'],
116 ['docs/man/ipython.1'],
117 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
117 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
118 ]
118 ]
119
119
120
120
121 [ target_update(*t) for t in to_update ]
121 [ target_update(*t) for t in to_update ]
122
122
123 #---------------------------------------------------------------------------
123 #---------------------------------------------------------------------------
124 # Find all the packages, package data, and data_files
124 # Find all the packages, package data, and data_files
125 #---------------------------------------------------------------------------
125 #---------------------------------------------------------------------------
126
126
127 packages = find_packages()
127 packages = find_packages()
128 package_data = find_package_data()
128 package_data = find_package_data()
129
129
130 data_files = find_data_files()
130 data_files = find_data_files()
131
131
132 setup_args['packages'] = packages
132 setup_args['packages'] = packages
133 setup_args['package_data'] = package_data
133 setup_args['package_data'] = package_data
134 setup_args['data_files'] = data_files
134 setup_args['data_files'] = data_files
135
135
136 #---------------------------------------------------------------------------
136 #---------------------------------------------------------------------------
137 # custom distutils commands
137 # custom distutils commands
138 #---------------------------------------------------------------------------
138 #---------------------------------------------------------------------------
139 # imports here, so they are after setuptools import if there was one
139 # imports here, so they are after setuptools import if there was one
140 from distutils.command.sdist import sdist
140 from distutils.command.sdist import sdist
141
141
142 setup_args['cmdclass'] = {
142 setup_args['cmdclass'] = {
143 'build_py': \
143 'build_py': \
144 check_package_data_first(git_prebuild('IPython')),
144 check_package_data_first(git_prebuild('IPython')),
145 'sdist' : git_prebuild('IPython', sdist),
145 'sdist' : git_prebuild('IPython', sdist),
146 'symlink': install_symlinked,
146 'symlink': install_symlinked,
147 'install_lib_symlink': install_lib_symlink,
147 'install_lib_symlink': install_lib_symlink,
148 'install_scripts_sym': install_scripts_for_symlink,
148 'install_scripts_sym': install_scripts_for_symlink,
149 'unsymlink': unsymlink,
149 'unsymlink': unsymlink,
150 }
150 }
151
151
152
152
153 #---------------------------------------------------------------------------
153 #---------------------------------------------------------------------------
154 # Handle scripts, dependencies, and setuptools specific things
154 # Handle scripts, dependencies, and setuptools specific things
155 #---------------------------------------------------------------------------
155 #---------------------------------------------------------------------------
156
156
157 # For some commands, use setuptools. Note that we do NOT list install here!
157 # For some commands, use setuptools. Note that we do NOT list install here!
158 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
158 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
159 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
159 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
160 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
160 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
161 'egg_info', 'easy_install', 'upload', 'install_egg_info',
161 'egg_info', 'easy_install', 'upload', 'install_egg_info',
162 }
162 }
163
163
164 if len(needs_setuptools.intersection(sys.argv)) > 0:
164 if len(needs_setuptools.intersection(sys.argv)) > 0:
165 import setuptools
165 import setuptools
166
166
167 # This dict is used for passing extra arguments that are setuptools
167 # This dict is used for passing extra arguments that are setuptools
168 # specific to setup
168 # specific to setup
169 setuptools_extra_args = {}
169 setuptools_extra_args = {}
170
170
171 # setuptools requirements
171 # setuptools requirements
172
172
173 extras_require = dict(
173 extras_require = dict(
174 parallel=["ipyparallel"],
174 parallel=["ipyparallel"],
175 qtconsole=["qtconsole"],
175 qtconsole=["qtconsole"],
176 doc=["Sphinx>=1.3"],
176 doc=["Sphinx>=1.3"],
177 test=[
177 test=[
178 "pytest",
178 "pytest",
179 "testpath",
179 "testpath",
180 "pygments",
180 "pygments",
181 ],
182 test_extra=[
183 "pytest",
184 "testpath",
185 "curio",
186 "matplotlib!=3.2.0",
181 "nbformat",
187 "nbformat",
182 "ipykernel",
188 "ipykernel",
183 "numpy>=1.17",
189 "numpy>=1.17",
190 "pandas",
191 "pygments",
192 "trio",
184 ],
193 ],
185 terminal=[],
194 terminal=[],
186 kernel=["ipykernel"],
195 kernel=["ipykernel"],
187 nbformat=["nbformat"],
196 nbformat=["nbformat"],
188 notebook=["notebook", "ipywidgets"],
197 notebook=["notebook", "ipywidgets"],
189 nbconvert=["nbconvert"],
198 nbconvert=["nbconvert"],
190 )
199 )
191
200
192 install_requires = [
201 install_requires = [
193 "setuptools>=18.5",
202 "setuptools>=18.5",
194 "jedi>=0.16",
203 "jedi>=0.16",
195 "decorator",
204 "decorator",
196 "pickleshare",
205 "pickleshare",
197 "traitlets>=4.2",
206 "traitlets>=4.2",
198 "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1",
207 "prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1",
199 "pygments",
208 "pygments",
200 "backcall",
209 "backcall",
201 "stack_data",
210 "stack_data",
202 "matplotlib-inline",
211 "matplotlib-inline",
203 ]
212 ]
204
213
205 # Platform-specific dependencies:
214 # Platform-specific dependencies:
206 # This is the correct way to specify these,
215 # This is the correct way to specify these,
207 # but requires pip >= 6. pip < 6 ignores these.
216 # but requires pip >= 6. pip < 6 ignores these.
208
217
209 extras_require.update(
218 extras_require.update(
210 {
219 {
211 ':sys_platform != "win32"': ["pexpect>4.3"],
220 ':sys_platform != "win32"': ["pexpect>4.3"],
212 ':sys_platform == "darwin"': ["appnope"],
221 ':sys_platform == "darwin"': ["appnope"],
213 ':sys_platform == "win32"': ["colorama"],
222 ':sys_platform == "win32"': ["colorama"],
214 }
223 }
215 )
224 )
216 # FIXME: re-specify above platform dependencies for pip < 6
225 # FIXME: re-specify above platform dependencies for pip < 6
217 # These would result in non-portable bdists.
226 # These would result in non-portable bdists.
218 if not any(arg.startswith('bdist') for arg in sys.argv):
227 if not any(arg.startswith('bdist') for arg in sys.argv):
219 if sys.platform == 'darwin':
228 if sys.platform == 'darwin':
220 install_requires.extend(['appnope'])
229 install_requires.extend(['appnope'])
221
230
222 if not sys.platform.startswith("win"):
231 if not sys.platform.startswith("win"):
223 install_requires.append("pexpect>4.3")
232 install_requires.append("pexpect>4.3")
224
233
225 # workaround pypa/setuptools#147, where setuptools misspells
234 # workaround pypa/setuptools#147, where setuptools misspells
226 # platform_python_implementation as python_implementation
235 # platform_python_implementation as python_implementation
227 if 'setuptools' in sys.modules:
236 if 'setuptools' in sys.modules:
228 for key in list(extras_require):
237 for key in list(extras_require):
229 if 'platform_python_implementation' in key:
238 if 'platform_python_implementation' in key:
230 new_key = key.replace('platform_python_implementation', 'python_implementation')
239 new_key = key.replace('platform_python_implementation', 'python_implementation')
231 extras_require[new_key] = extras_require.pop(key)
240 extras_require[new_key] = extras_require.pop(key)
232
241
233 everything = set()
242 everything = set()
234 for key, deps in extras_require.items():
243 for key, deps in extras_require.items():
235 if ':' not in key:
244 if ':' not in key:
236 everything.update(deps)
245 everything.update(deps)
237 extras_require['all'] = list(sorted(everything))
246 extras_require['all'] = list(sorted(everything))
238
247
239 if 'setuptools' in sys.modules:
248 if 'setuptools' in sys.modules:
240 setuptools_extra_args['python_requires'] = '>=3.7'
249 setuptools_extra_args['python_requires'] = '>=3.7'
241 setuptools_extra_args['zip_safe'] = False
250 setuptools_extra_args['zip_safe'] = False
242 setuptools_extra_args['entry_points'] = {
251 setuptools_extra_args['entry_points'] = {
243 'console_scripts': find_entry_points(),
252 'console_scripts': find_entry_points(),
244 'pygments.lexers': [
253 'pygments.lexers': [
245 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
254 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
246 'ipython = IPython.lib.lexers:IPythonLexer',
255 'ipython = IPython.lib.lexers:IPythonLexer',
247 'ipython3 = IPython.lib.lexers:IPython3Lexer',
256 'ipython3 = IPython.lib.lexers:IPython3Lexer',
248 ],
257 ],
249 }
258 }
250 setup_args['extras_require'] = extras_require
259 setup_args['extras_require'] = extras_require
251 setup_args['install_requires'] = install_requires
260 setup_args['install_requires'] = install_requires
252
261
253 else:
262 else:
254 # scripts has to be a non-empty list, or install_scripts isn't called
263 # scripts has to be a non-empty list, or install_scripts isn't called
255 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
264 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
256
265
257 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
266 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
258
267
259 #---------------------------------------------------------------------------
268 #---------------------------------------------------------------------------
260 # Do the actual setup now
269 # Do the actual setup now
261 #---------------------------------------------------------------------------
270 #---------------------------------------------------------------------------
262
271
263 setup_args.update(setuptools_extra_args)
272 setup_args.update(setuptools_extra_args)
264
273
265
274
266
275
267 def main():
276 def main():
268 setup(**setup_args)
277 setup(**setup_args)
269
278
270 if __name__ == '__main__':
279 if __name__ == '__main__':
271 main()
280 main()
General Comments 0
You need to be logged in to leave comments. Login now