##// END OF EJS Templates
don't install test extension...
Min RK -
Show More
@@ -1,1000 +1,996 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions.
2 """Tests for various magic functions.
3
3
4 Needs to be run by nose (to make ipython session available).
4 Needs to be run by nose (to make ipython session available).
5 """
5 """
6 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import io
8 import io
9 import os
9 import os
10 import sys
10 import sys
11 import warnings
11 import warnings
12 from unittest import TestCase
12 from unittest import TestCase
13
13
14 try:
14 try:
15 from importlib import invalidate_caches # Required from Python 3.3
15 from importlib import invalidate_caches # Required from Python 3.3
16 except ImportError:
16 except ImportError:
17 def invalidate_caches():
17 def invalidate_caches():
18 pass
18 pass
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 from IPython import get_ipython
22 from IPython import get_ipython
23 from IPython.core import magic
23 from IPython.core import magic
24 from IPython.core.error import UsageError
24 from IPython.core.error import UsageError
25 from IPython.core.magic import (Magics, magics_class, line_magic,
25 from IPython.core.magic import (Magics, magics_class, line_magic,
26 cell_magic,
26 cell_magic,
27 register_line_magic, register_cell_magic)
27 register_line_magic, register_cell_magic)
28 from IPython.core.magics import execution, script, code
28 from IPython.core.magics import execution, script, code
29 from IPython.testing import decorators as dec
29 from IPython.testing import decorators as dec
30 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
31 from IPython.utils import py3compat
31 from IPython.utils import py3compat
32 from IPython.utils.io import capture_output
32 from IPython.utils.io import capture_output
33 from IPython.utils.tempdir import TemporaryDirectory
33 from IPython.utils.tempdir import TemporaryDirectory
34 from IPython.utils.process import find_cmd
34 from IPython.utils.process import find_cmd
35
35
36 if py3compat.PY3:
36 if py3compat.PY3:
37 from io import StringIO
37 from io import StringIO
38 else:
38 else:
39 from StringIO import StringIO
39 from StringIO import StringIO
40
40
41
41
42 @magic.magics_class
42 @magic.magics_class
43 class DummyMagics(magic.Magics): pass
43 class DummyMagics(magic.Magics): pass
44
44
45 def test_extract_code_ranges():
45 def test_extract_code_ranges():
46 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
46 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
47 expected = [(0, 1),
47 expected = [(0, 1),
48 (2, 3),
48 (2, 3),
49 (4, 6),
49 (4, 6),
50 (6, 9),
50 (6, 9),
51 (9, 14),
51 (9, 14),
52 (16, None),
52 (16, None),
53 (None, 9),
53 (None, 9),
54 (9, None),
54 (9, None),
55 (None, 13),
55 (None, 13),
56 (None, None)]
56 (None, None)]
57 actual = list(code.extract_code_ranges(instr))
57 actual = list(code.extract_code_ranges(instr))
58 nt.assert_equal(actual, expected)
58 nt.assert_equal(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 nt.assert_equal(code.extract_symbols(source, symbols), exp)
70 nt.assert_equal(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 nt.assert_raises(SyntaxError):
78 with nt.assert_raises(SyntaxError):
79 code.extract_symbols(source, "hello")
79 code.extract_symbols(source, "hello")
80
80
81 def test_config():
81 def test_config():
82 """ test that config magic does not raise
82 """ test that config magic does not raise
83 can happen if Configurable init is moved too early into
83 can happen if Configurable init is moved too early into
84 Magics.__init__ as then a Config object will be registerd as a
84 Magics.__init__ as then a Config object will be registerd as a
85 magic.
85 magic.
86 """
86 """
87 ## should not raise.
87 ## should not raise.
88 _ip.magic('config')
88 _ip.magic('config')
89
89
90 def test_rehashx():
90 def test_rehashx():
91 # clear up everything
91 # clear up everything
92 _ip = get_ipython()
92 _ip = get_ipython()
93 _ip.alias_manager.clear_aliases()
93 _ip.alias_manager.clear_aliases()
94 del _ip.db['syscmdlist']
94 del _ip.db['syscmdlist']
95
95
96 _ip.magic('rehashx')
96 _ip.magic('rehashx')
97 # Practically ALL ipython development systems will have more than 10 aliases
97 # Practically ALL ipython development systems will have more than 10 aliases
98
98
99 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
99 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
100 for name, cmd in _ip.alias_manager.aliases:
100 for name, cmd in _ip.alias_manager.aliases:
101 # we must strip dots from alias names
101 # we must strip dots from alias names
102 nt.assert_not_in('.', name)
102 nt.assert_not_in('.', name)
103
103
104 # rehashx must fill up syscmdlist
104 # rehashx must fill up syscmdlist
105 scoms = _ip.db['syscmdlist']
105 scoms = _ip.db['syscmdlist']
106 nt.assert_true(len(scoms) > 10)
106 nt.assert_true(len(scoms) > 10)
107
107
108
108
109 def test_magic_parse_options():
109 def test_magic_parse_options():
110 """Test that we don't mangle paths when parsing magic options."""
110 """Test that we don't mangle paths when parsing magic options."""
111 ip = get_ipython()
111 ip = get_ipython()
112 path = 'c:\\x'
112 path = 'c:\\x'
113 m = DummyMagics(ip)
113 m = DummyMagics(ip)
114 opts = m.parse_options('-f %s' % path,'f:')[0]
114 opts = m.parse_options('-f %s' % path,'f:')[0]
115 # argv splitting is os-dependent
115 # argv splitting is os-dependent
116 if os.name == 'posix':
116 if os.name == 'posix':
117 expected = 'c:x'
117 expected = 'c:x'
118 else:
118 else:
119 expected = path
119 expected = path
120 nt.assert_equal(opts['f'], expected)
120 nt.assert_equal(opts['f'], expected)
121
121
122 def test_magic_parse_long_options():
122 def test_magic_parse_long_options():
123 """Magic.parse_options can handle --foo=bar long options"""
123 """Magic.parse_options can handle --foo=bar long options"""
124 ip = get_ipython()
124 ip = get_ipython()
125 m = DummyMagics(ip)
125 m = DummyMagics(ip)
126 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
126 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
127 nt.assert_in('foo', opts)
127 nt.assert_in('foo', opts)
128 nt.assert_in('bar', opts)
128 nt.assert_in('bar', opts)
129 nt.assert_equal(opts['bar'], "bubble")
129 nt.assert_equal(opts['bar'], "bubble")
130
130
131
131
132 @dec.skip_without('sqlite3')
132 @dec.skip_without('sqlite3')
133 def doctest_hist_f():
133 def doctest_hist_f():
134 """Test %hist -f with temporary filename.
134 """Test %hist -f with temporary filename.
135
135
136 In [9]: import tempfile
136 In [9]: import tempfile
137
137
138 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
138 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
139
139
140 In [11]: %hist -nl -f $tfile 3
140 In [11]: %hist -nl -f $tfile 3
141
141
142 In [13]: import os; os.unlink(tfile)
142 In [13]: import os; os.unlink(tfile)
143 """
143 """
144
144
145
145
146 @dec.skip_without('sqlite3')
146 @dec.skip_without('sqlite3')
147 def doctest_hist_r():
147 def doctest_hist_r():
148 """Test %hist -r
148 """Test %hist -r
149
149
150 XXX - This test is not recording the output correctly. For some reason, in
150 XXX - This test is not recording the output correctly. For some reason, in
151 testing mode the raw history isn't getting populated. No idea why.
151 testing mode the raw history isn't getting populated. No idea why.
152 Disabling the output checking for now, though at least we do run it.
152 Disabling the output checking for now, though at least we do run it.
153
153
154 In [1]: 'hist' in _ip.lsmagic()
154 In [1]: 'hist' in _ip.lsmagic()
155 Out[1]: True
155 Out[1]: True
156
156
157 In [2]: x=1
157 In [2]: x=1
158
158
159 In [3]: %hist -rl 2
159 In [3]: %hist -rl 2
160 x=1 # random
160 x=1 # random
161 %hist -r 2
161 %hist -r 2
162 """
162 """
163
163
164
164
165 @dec.skip_without('sqlite3')
165 @dec.skip_without('sqlite3')
166 def doctest_hist_op():
166 def doctest_hist_op():
167 """Test %hist -op
167 """Test %hist -op
168
168
169 In [1]: class b(float):
169 In [1]: class b(float):
170 ...: pass
170 ...: pass
171 ...:
171 ...:
172
172
173 In [2]: class s(object):
173 In [2]: class s(object):
174 ...: def __str__(self):
174 ...: def __str__(self):
175 ...: return 's'
175 ...: return 's'
176 ...:
176 ...:
177
177
178 In [3]:
178 In [3]:
179
179
180 In [4]: class r(b):
180 In [4]: class r(b):
181 ...: def __repr__(self):
181 ...: def __repr__(self):
182 ...: return 'r'
182 ...: return 'r'
183 ...:
183 ...:
184
184
185 In [5]: class sr(s,r): pass
185 In [5]: class sr(s,r): pass
186 ...:
186 ...:
187
187
188 In [6]:
188 In [6]:
189
189
190 In [7]: bb=b()
190 In [7]: bb=b()
191
191
192 In [8]: ss=s()
192 In [8]: ss=s()
193
193
194 In [9]: rr=r()
194 In [9]: rr=r()
195
195
196 In [10]: ssrr=sr()
196 In [10]: ssrr=sr()
197
197
198 In [11]: 4.5
198 In [11]: 4.5
199 Out[11]: 4.5
199 Out[11]: 4.5
200
200
201 In [12]: str(ss)
201 In [12]: str(ss)
202 Out[12]: 's'
202 Out[12]: 's'
203
203
204 In [13]:
204 In [13]:
205
205
206 In [14]: %hist -op
206 In [14]: %hist -op
207 >>> class b:
207 >>> class b:
208 ... pass
208 ... pass
209 ...
209 ...
210 >>> class s(b):
210 >>> class s(b):
211 ... def __str__(self):
211 ... def __str__(self):
212 ... return 's'
212 ... return 's'
213 ...
213 ...
214 >>>
214 >>>
215 >>> class r(b):
215 >>> class r(b):
216 ... def __repr__(self):
216 ... def __repr__(self):
217 ... return 'r'
217 ... return 'r'
218 ...
218 ...
219 >>> class sr(s,r): pass
219 >>> class sr(s,r): pass
220 >>>
220 >>>
221 >>> bb=b()
221 >>> bb=b()
222 >>> ss=s()
222 >>> ss=s()
223 >>> rr=r()
223 >>> rr=r()
224 >>> ssrr=sr()
224 >>> ssrr=sr()
225 >>> 4.5
225 >>> 4.5
226 4.5
226 4.5
227 >>> str(ss)
227 >>> str(ss)
228 's'
228 's'
229 >>>
229 >>>
230 """
230 """
231
231
232 def test_hist_pof():
232 def test_hist_pof():
233 ip = get_ipython()
233 ip = get_ipython()
234 ip.run_cell(u"1+2", store_history=True)
234 ip.run_cell(u"1+2", store_history=True)
235 #raise Exception(ip.history_manager.session_number)
235 #raise Exception(ip.history_manager.session_number)
236 #raise Exception(list(ip.history_manager._get_range_session()))
236 #raise Exception(list(ip.history_manager._get_range_session()))
237 with TemporaryDirectory() as td:
237 with TemporaryDirectory() as td:
238 tf = os.path.join(td, 'hist.py')
238 tf = os.path.join(td, 'hist.py')
239 ip.run_line_magic('history', '-pof %s' % tf)
239 ip.run_line_magic('history', '-pof %s' % tf)
240 assert os.path.isfile(tf)
240 assert os.path.isfile(tf)
241
241
242
242
243 @dec.skip_without('sqlite3')
243 @dec.skip_without('sqlite3')
244 def test_macro():
244 def test_macro():
245 ip = get_ipython()
245 ip = get_ipython()
246 ip.history_manager.reset() # Clear any existing history.
246 ip.history_manager.reset() # Clear any existing history.
247 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
247 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
248 for i, cmd in enumerate(cmds, start=1):
248 for i, cmd in enumerate(cmds, start=1):
249 ip.history_manager.store_inputs(i, cmd)
249 ip.history_manager.store_inputs(i, cmd)
250 ip.magic("macro test 1-3")
250 ip.magic("macro test 1-3")
251 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
251 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
252
252
253 # List macros
253 # List macros
254 nt.assert_in("test", ip.magic("macro"))
254 nt.assert_in("test", ip.magic("macro"))
255
255
256
256
257 @dec.skip_without('sqlite3')
257 @dec.skip_without('sqlite3')
258 def test_macro_run():
258 def test_macro_run():
259 """Test that we can run a multi-line macro successfully."""
259 """Test that we can run a multi-line macro successfully."""
260 ip = get_ipython()
260 ip = get_ipython()
261 ip.history_manager.reset()
261 ip.history_manager.reset()
262 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
262 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
263 "%macro test 2-3"]
263 "%macro test 2-3"]
264 for cmd in cmds:
264 for cmd in cmds:
265 ip.run_cell(cmd, store_history=True)
265 ip.run_cell(cmd, store_history=True)
266 nt.assert_equal(ip.user_ns["test"].value,
266 nt.assert_equal(ip.user_ns["test"].value,
267 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
267 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
268 with tt.AssertPrints("12"):
268 with tt.AssertPrints("12"):
269 ip.run_cell("test")
269 ip.run_cell("test")
270 with tt.AssertPrints("13"):
270 with tt.AssertPrints("13"):
271 ip.run_cell("test")
271 ip.run_cell("test")
272
272
273
273
274 def test_magic_magic():
274 def test_magic_magic():
275 """Test %magic"""
275 """Test %magic"""
276 ip = get_ipython()
276 ip = get_ipython()
277 with capture_output() as captured:
277 with capture_output() as captured:
278 ip.magic("magic")
278 ip.magic("magic")
279
279
280 stdout = captured.stdout
280 stdout = captured.stdout
281 nt.assert_in('%magic', stdout)
281 nt.assert_in('%magic', stdout)
282 nt.assert_in('IPython', stdout)
282 nt.assert_in('IPython', stdout)
283 nt.assert_in('Available', stdout)
283 nt.assert_in('Available', stdout)
284
284
285
285
286 @dec.skipif_not_numpy
286 @dec.skipif_not_numpy
287 def test_numpy_reset_array_undec():
287 def test_numpy_reset_array_undec():
288 "Test '%reset array' functionality"
288 "Test '%reset array' functionality"
289 _ip.ex('import numpy as np')
289 _ip.ex('import numpy as np')
290 _ip.ex('a = np.empty(2)')
290 _ip.ex('a = np.empty(2)')
291 nt.assert_in('a', _ip.user_ns)
291 nt.assert_in('a', _ip.user_ns)
292 _ip.magic('reset -f array')
292 _ip.magic('reset -f array')
293 nt.assert_not_in('a', _ip.user_ns)
293 nt.assert_not_in('a', _ip.user_ns)
294
294
295 def test_reset_out():
295 def test_reset_out():
296 "Test '%reset out' magic"
296 "Test '%reset out' magic"
297 _ip.run_cell("parrot = 'dead'", store_history=True)
297 _ip.run_cell("parrot = 'dead'", store_history=True)
298 # test '%reset -f out', make an Out prompt
298 # test '%reset -f out', make an Out prompt
299 _ip.run_cell("parrot", store_history=True)
299 _ip.run_cell("parrot", store_history=True)
300 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
300 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
301 _ip.magic('reset -f out')
301 _ip.magic('reset -f out')
302 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
302 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
303 nt.assert_equal(len(_ip.user_ns['Out']), 0)
303 nt.assert_equal(len(_ip.user_ns['Out']), 0)
304
304
305 def test_reset_in():
305 def test_reset_in():
306 "Test '%reset in' magic"
306 "Test '%reset in' magic"
307 # test '%reset -f in'
307 # test '%reset -f in'
308 _ip.run_cell("parrot", store_history=True)
308 _ip.run_cell("parrot", store_history=True)
309 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
309 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
310 _ip.magic('%reset -f in')
310 _ip.magic('%reset -f in')
311 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
311 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
312 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
312 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
313
313
314 def test_reset_dhist():
314 def test_reset_dhist():
315 "Test '%reset dhist' magic"
315 "Test '%reset dhist' magic"
316 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
316 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
317 _ip.magic('cd ' + os.path.dirname(nt.__file__))
317 _ip.magic('cd ' + os.path.dirname(nt.__file__))
318 _ip.magic('cd -')
318 _ip.magic('cd -')
319 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
319 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
320 _ip.magic('reset -f dhist')
320 _ip.magic('reset -f dhist')
321 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
321 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
322 _ip.run_cell("_dh = [d for d in tmp]") #restore
322 _ip.run_cell("_dh = [d for d in tmp]") #restore
323
323
324 def test_reset_in_length():
324 def test_reset_in_length():
325 "Test that '%reset in' preserves In[] length"
325 "Test that '%reset in' preserves In[] length"
326 _ip.run_cell("print 'foo'")
326 _ip.run_cell("print 'foo'")
327 _ip.run_cell("reset -f in")
327 _ip.run_cell("reset -f in")
328 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
328 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
329
329
330 def test_tb_syntaxerror():
330 def test_tb_syntaxerror():
331 """test %tb after a SyntaxError"""
331 """test %tb after a SyntaxError"""
332 ip = get_ipython()
332 ip = get_ipython()
333 ip.run_cell("for")
333 ip.run_cell("for")
334
334
335 # trap and validate stdout
335 # trap and validate stdout
336 save_stdout = sys.stdout
336 save_stdout = sys.stdout
337 try:
337 try:
338 sys.stdout = StringIO()
338 sys.stdout = StringIO()
339 ip.run_cell("%tb")
339 ip.run_cell("%tb")
340 out = sys.stdout.getvalue()
340 out = sys.stdout.getvalue()
341 finally:
341 finally:
342 sys.stdout = save_stdout
342 sys.stdout = save_stdout
343 # trim output, and only check the last line
343 # trim output, and only check the last line
344 last_line = out.rstrip().splitlines()[-1].strip()
344 last_line = out.rstrip().splitlines()[-1].strip()
345 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
345 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
346
346
347
347
348 def test_time():
348 def test_time():
349 ip = get_ipython()
349 ip = get_ipython()
350
350
351 with tt.AssertPrints("Wall time: "):
351 with tt.AssertPrints("Wall time: "):
352 ip.run_cell("%time None")
352 ip.run_cell("%time None")
353
353
354 ip.run_cell("def f(kmjy):\n"
354 ip.run_cell("def f(kmjy):\n"
355 " %time print (2*kmjy)")
355 " %time print (2*kmjy)")
356
356
357 with tt.AssertPrints("Wall time: "):
357 with tt.AssertPrints("Wall time: "):
358 with tt.AssertPrints("hihi", suppress=False):
358 with tt.AssertPrints("hihi", suppress=False):
359 ip.run_cell("f('hi')")
359 ip.run_cell("f('hi')")
360
360
361
361
362 @dec.skip_win32
362 @dec.skip_win32
363 def test_time2():
363 def test_time2():
364 ip = get_ipython()
364 ip = get_ipython()
365
365
366 with tt.AssertPrints("CPU times: user "):
366 with tt.AssertPrints("CPU times: user "):
367 ip.run_cell("%time None")
367 ip.run_cell("%time None")
368
368
369 def test_time3():
369 def test_time3():
370 """Erroneous magic function calls, issue gh-3334"""
370 """Erroneous magic function calls, issue gh-3334"""
371 ip = get_ipython()
371 ip = get_ipython()
372 ip.user_ns.pop('run', None)
372 ip.user_ns.pop('run', None)
373
373
374 with tt.AssertNotPrints("not found", channel='stderr'):
374 with tt.AssertNotPrints("not found", channel='stderr'):
375 ip.run_cell("%%time\n"
375 ip.run_cell("%%time\n"
376 "run = 0\n"
376 "run = 0\n"
377 "run += 1")
377 "run += 1")
378
378
379 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
379 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
380 def test_time_futures():
380 def test_time_futures():
381 "Test %time with __future__ environments"
381 "Test %time with __future__ environments"
382 ip = get_ipython()
382 ip = get_ipython()
383 ip.autocall = 0
383 ip.autocall = 0
384 ip.run_cell("from __future__ import division")
384 ip.run_cell("from __future__ import division")
385 with tt.AssertPrints('0.25'):
385 with tt.AssertPrints('0.25'):
386 ip.run_line_magic('time', 'print(1/4)')
386 ip.run_line_magic('time', 'print(1/4)')
387 ip.compile.reset_compiler_flags()
387 ip.compile.reset_compiler_flags()
388 with tt.AssertNotPrints('0.25'):
388 with tt.AssertNotPrints('0.25'):
389 ip.run_line_magic('time', 'print(1/4)')
389 ip.run_line_magic('time', 'print(1/4)')
390
390
391 def test_doctest_mode():
391 def test_doctest_mode():
392 "Toggle doctest_mode twice, it should be a no-op and run without error"
392 "Toggle doctest_mode twice, it should be a no-op and run without error"
393 _ip.magic('doctest_mode')
393 _ip.magic('doctest_mode')
394 _ip.magic('doctest_mode')
394 _ip.magic('doctest_mode')
395
395
396
396
397 def test_parse_options():
397 def test_parse_options():
398 """Tests for basic options parsing in magics."""
398 """Tests for basic options parsing in magics."""
399 # These are only the most minimal of tests, more should be added later. At
399 # These are only the most minimal of tests, more should be added later. At
400 # the very least we check that basic text/unicode calls work OK.
400 # the very least we check that basic text/unicode calls work OK.
401 m = DummyMagics(_ip)
401 m = DummyMagics(_ip)
402 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
402 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
403 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
403 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
404
404
405
405
406 def test_dirops():
406 def test_dirops():
407 """Test various directory handling operations."""
407 """Test various directory handling operations."""
408 # curpath = lambda :os.path.splitdrive(py3compat.getcwd())[1].replace('\\','/')
408 # curpath = lambda :os.path.splitdrive(py3compat.getcwd())[1].replace('\\','/')
409 curpath = py3compat.getcwd
409 curpath = py3compat.getcwd
410 startdir = py3compat.getcwd()
410 startdir = py3compat.getcwd()
411 ipdir = os.path.realpath(_ip.ipython_dir)
411 ipdir = os.path.realpath(_ip.ipython_dir)
412 try:
412 try:
413 _ip.magic('cd "%s"' % ipdir)
413 _ip.magic('cd "%s"' % ipdir)
414 nt.assert_equal(curpath(), ipdir)
414 nt.assert_equal(curpath(), ipdir)
415 _ip.magic('cd -')
415 _ip.magic('cd -')
416 nt.assert_equal(curpath(), startdir)
416 nt.assert_equal(curpath(), startdir)
417 _ip.magic('pushd "%s"' % ipdir)
417 _ip.magic('pushd "%s"' % ipdir)
418 nt.assert_equal(curpath(), ipdir)
418 nt.assert_equal(curpath(), ipdir)
419 _ip.magic('popd')
419 _ip.magic('popd')
420 nt.assert_equal(curpath(), startdir)
420 nt.assert_equal(curpath(), startdir)
421 finally:
421 finally:
422 os.chdir(startdir)
422 os.chdir(startdir)
423
423
424
424
425 def test_xmode():
425 def test_xmode():
426 # Calling xmode three times should be a no-op
426 # Calling xmode three times should be a no-op
427 xmode = _ip.InteractiveTB.mode
427 xmode = _ip.InteractiveTB.mode
428 for i in range(3):
428 for i in range(3):
429 _ip.magic("xmode")
429 _ip.magic("xmode")
430 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
430 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
431
431
432 def test_reset_hard():
432 def test_reset_hard():
433 monitor = []
433 monitor = []
434 class A(object):
434 class A(object):
435 def __del__(self):
435 def __del__(self):
436 monitor.append(1)
436 monitor.append(1)
437 def __repr__(self):
437 def __repr__(self):
438 return "<A instance>"
438 return "<A instance>"
439
439
440 _ip.user_ns["a"] = A()
440 _ip.user_ns["a"] = A()
441 _ip.run_cell("a")
441 _ip.run_cell("a")
442
442
443 nt.assert_equal(monitor, [])
443 nt.assert_equal(monitor, [])
444 _ip.magic("reset -f")
444 _ip.magic("reset -f")
445 nt.assert_equal(monitor, [1])
445 nt.assert_equal(monitor, [1])
446
446
447 class TestXdel(tt.TempFileMixin):
447 class TestXdel(tt.TempFileMixin):
448 def test_xdel(self):
448 def test_xdel(self):
449 """Test that references from %run are cleared by xdel."""
449 """Test that references from %run are cleared by xdel."""
450 src = ("class A(object):\n"
450 src = ("class A(object):\n"
451 " monitor = []\n"
451 " monitor = []\n"
452 " def __del__(self):\n"
452 " def __del__(self):\n"
453 " self.monitor.append(1)\n"
453 " self.monitor.append(1)\n"
454 "a = A()\n")
454 "a = A()\n")
455 self.mktmp(src)
455 self.mktmp(src)
456 # %run creates some hidden references...
456 # %run creates some hidden references...
457 _ip.magic("run %s" % self.fname)
457 _ip.magic("run %s" % self.fname)
458 # ... as does the displayhook.
458 # ... as does the displayhook.
459 _ip.run_cell("a")
459 _ip.run_cell("a")
460
460
461 monitor = _ip.user_ns["A"].monitor
461 monitor = _ip.user_ns["A"].monitor
462 nt.assert_equal(monitor, [])
462 nt.assert_equal(monitor, [])
463
463
464 _ip.magic("xdel a")
464 _ip.magic("xdel a")
465
465
466 # Check that a's __del__ method has been called.
466 # Check that a's __del__ method has been called.
467 nt.assert_equal(monitor, [1])
467 nt.assert_equal(monitor, [1])
468
468
469 def doctest_who():
469 def doctest_who():
470 """doctest for %who
470 """doctest for %who
471
471
472 In [1]: %reset -f
472 In [1]: %reset -f
473
473
474 In [2]: alpha = 123
474 In [2]: alpha = 123
475
475
476 In [3]: beta = 'beta'
476 In [3]: beta = 'beta'
477
477
478 In [4]: %who int
478 In [4]: %who int
479 alpha
479 alpha
480
480
481 In [5]: %who str
481 In [5]: %who str
482 beta
482 beta
483
483
484 In [6]: %whos
484 In [6]: %whos
485 Variable Type Data/Info
485 Variable Type Data/Info
486 ----------------------------
486 ----------------------------
487 alpha int 123
487 alpha int 123
488 beta str beta
488 beta str beta
489
489
490 In [7]: %who_ls
490 In [7]: %who_ls
491 Out[7]: ['alpha', 'beta']
491 Out[7]: ['alpha', 'beta']
492 """
492 """
493
493
494 def test_whos():
494 def test_whos():
495 """Check that whos is protected against objects where repr() fails."""
495 """Check that whos is protected against objects where repr() fails."""
496 class A(object):
496 class A(object):
497 def __repr__(self):
497 def __repr__(self):
498 raise Exception()
498 raise Exception()
499 _ip.user_ns['a'] = A()
499 _ip.user_ns['a'] = A()
500 _ip.magic("whos")
500 _ip.magic("whos")
501
501
502 @py3compat.u_format
502 @py3compat.u_format
503 def doctest_precision():
503 def doctest_precision():
504 """doctest for %precision
504 """doctest for %precision
505
505
506 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
506 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
507
507
508 In [2]: %precision 5
508 In [2]: %precision 5
509 Out[2]: {u}'%.5f'
509 Out[2]: {u}'%.5f'
510
510
511 In [3]: f.float_format
511 In [3]: f.float_format
512 Out[3]: {u}'%.5f'
512 Out[3]: {u}'%.5f'
513
513
514 In [4]: %precision %e
514 In [4]: %precision %e
515 Out[4]: {u}'%e'
515 Out[4]: {u}'%e'
516
516
517 In [5]: f(3.1415927)
517 In [5]: f(3.1415927)
518 Out[5]: {u}'3.141593e+00'
518 Out[5]: {u}'3.141593e+00'
519 """
519 """
520
520
521 def test_psearch():
521 def test_psearch():
522 with tt.AssertPrints("dict.fromkeys"):
522 with tt.AssertPrints("dict.fromkeys"):
523 _ip.run_cell("dict.fr*?")
523 _ip.run_cell("dict.fr*?")
524
524
525 def test_timeit_shlex():
525 def test_timeit_shlex():
526 """test shlex issues with timeit (#1109)"""
526 """test shlex issues with timeit (#1109)"""
527 _ip.ex("def f(*a,**kw): pass")
527 _ip.ex("def f(*a,**kw): pass")
528 _ip.magic('timeit -n1 "this is a bug".count(" ")')
528 _ip.magic('timeit -n1 "this is a bug".count(" ")')
529 _ip.magic('timeit -r1 -n1 f(" ", 1)')
529 _ip.magic('timeit -r1 -n1 f(" ", 1)')
530 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
530 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
531 _ip.magic('timeit -r1 -n1 ("a " + "b")')
531 _ip.magic('timeit -r1 -n1 ("a " + "b")')
532 _ip.magic('timeit -r1 -n1 f("a " + "b")')
532 _ip.magic('timeit -r1 -n1 f("a " + "b")')
533 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
533 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
534
534
535
535
536 def test_timeit_arguments():
536 def test_timeit_arguments():
537 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
537 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
538 _ip.magic("timeit ('#')")
538 _ip.magic("timeit ('#')")
539
539
540
540
541 def test_timeit_special_syntax():
541 def test_timeit_special_syntax():
542 "Test %%timeit with IPython special syntax"
542 "Test %%timeit with IPython special syntax"
543 @register_line_magic
543 @register_line_magic
544 def lmagic(line):
544 def lmagic(line):
545 ip = get_ipython()
545 ip = get_ipython()
546 ip.user_ns['lmagic_out'] = line
546 ip.user_ns['lmagic_out'] = line
547
547
548 # line mode test
548 # line mode test
549 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
549 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
550 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
550 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
551 # cell mode test
551 # cell mode test
552 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
552 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
553 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
553 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
554
554
555 def test_timeit_return():
555 def test_timeit_return():
556 """
556 """
557 test wether timeit -o return object
557 test wether timeit -o return object
558 """
558 """
559
559
560 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
560 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
561 assert(res is not None)
561 assert(res is not None)
562
562
563 def test_timeit_quiet():
563 def test_timeit_quiet():
564 """
564 """
565 test quiet option of timeit magic
565 test quiet option of timeit magic
566 """
566 """
567 with tt.AssertNotPrints("loops"):
567 with tt.AssertNotPrints("loops"):
568 _ip.run_cell("%timeit -n1 -r1 -q 1")
568 _ip.run_cell("%timeit -n1 -r1 -q 1")
569
569
570 def test_timeit_return_quiet():
570 def test_timeit_return_quiet():
571 with tt.AssertNotPrints("loops"):
571 with tt.AssertNotPrints("loops"):
572 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
572 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
573 assert (res is not None)
573 assert (res is not None)
574
574
575 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
575 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
576 def test_timeit_futures():
576 def test_timeit_futures():
577 "Test %timeit with __future__ environments"
577 "Test %timeit with __future__ environments"
578 ip = get_ipython()
578 ip = get_ipython()
579 ip.run_cell("from __future__ import division")
579 ip.run_cell("from __future__ import division")
580 with tt.AssertPrints('0.25'):
580 with tt.AssertPrints('0.25'):
581 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
581 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
582 ip.compile.reset_compiler_flags()
582 ip.compile.reset_compiler_flags()
583 with tt.AssertNotPrints('0.25'):
583 with tt.AssertNotPrints('0.25'):
584 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
584 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
585
585
586 @dec.skipif(execution.profile is None)
586 @dec.skipif(execution.profile is None)
587 def test_prun_special_syntax():
587 def test_prun_special_syntax():
588 "Test %%prun with IPython special syntax"
588 "Test %%prun with IPython special syntax"
589 @register_line_magic
589 @register_line_magic
590 def lmagic(line):
590 def lmagic(line):
591 ip = get_ipython()
591 ip = get_ipython()
592 ip.user_ns['lmagic_out'] = line
592 ip.user_ns['lmagic_out'] = line
593
593
594 # line mode test
594 # line mode test
595 _ip.run_line_magic('prun', '-q %lmagic my line')
595 _ip.run_line_magic('prun', '-q %lmagic my line')
596 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
596 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
597 # cell mode test
597 # cell mode test
598 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
598 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
599 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
599 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
600
600
601 @dec.skipif(execution.profile is None)
601 @dec.skipif(execution.profile is None)
602 def test_prun_quotes():
602 def test_prun_quotes():
603 "Test that prun does not clobber string escapes (GH #1302)"
603 "Test that prun does not clobber string escapes (GH #1302)"
604 _ip.magic(r"prun -q x = '\t'")
604 _ip.magic(r"prun -q x = '\t'")
605 nt.assert_equal(_ip.user_ns['x'], '\t')
605 nt.assert_equal(_ip.user_ns['x'], '\t')
606
606
607 def test_extension():
607 def test_extension():
608 tmpdir = TemporaryDirectory()
608 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
609 orig_ipython_dir = _ip.ipython_dir
609 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
610 sys.path.insert(0, daft_path)
610 try:
611 try:
611 _ip.ipython_dir = tmpdir.name
612 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
613 url = os.path.join(os.path.dirname(__file__), "daft_extension")
614 _ip.system("%s -m pip install %s" % (sys.executable, url))
615 _ip.user_ns.pop('arq', None)
612 _ip.user_ns.pop('arq', None)
616 invalidate_caches() # Clear import caches
613 invalidate_caches() # Clear import caches
617 _ip.magic("load_ext daft_extension")
614 _ip.magic("load_ext daft_extension")
618 nt.assert_equal(_ip.user_ns['arq'], 185)
615 nt.assert_equal(_ip.user_ns['arq'], 185)
619 _ip.magic("unload_ext daft_extension")
616 _ip.magic("unload_ext daft_extension")
620 assert 'arq' not in _ip.user_ns
617 assert 'arq' not in _ip.user_ns
621 finally:
618 finally:
622 _ip.ipython_dir = orig_ipython_dir
619 sys.path.remove(daft_path)
623 tmpdir.cleanup()
624
620
625
621
626 @dec.skip_without('nbformat')
622 @dec.skip_without('nbformat')
627 def test_notebook_export_json():
623 def test_notebook_export_json():
628 _ip = get_ipython()
624 _ip = get_ipython()
629 _ip.history_manager.reset() # Clear any existing history.
625 _ip.history_manager.reset() # Clear any existing history.
630 cmds = [u"a=1", u"def b():\n return a**2", u"print('noël, été', b())"]
626 cmds = [u"a=1", u"def b():\n return a**2", u"print('noël, été', b())"]
631 for i, cmd in enumerate(cmds, start=1):
627 for i, cmd in enumerate(cmds, start=1):
632 _ip.history_manager.store_inputs(i, cmd)
628 _ip.history_manager.store_inputs(i, cmd)
633 with TemporaryDirectory() as td:
629 with TemporaryDirectory() as td:
634 outfile = os.path.join(td, "nb.ipynb")
630 outfile = os.path.join(td, "nb.ipynb")
635 _ip.magic("notebook -e %s" % outfile)
631 _ip.magic("notebook -e %s" % outfile)
636
632
637
633
638 class TestEnv(TestCase):
634 class TestEnv(TestCase):
639
635
640 def test_env(self):
636 def test_env(self):
641 env = _ip.magic("env")
637 env = _ip.magic("env")
642 self.assertTrue(isinstance(env, dict))
638 self.assertTrue(isinstance(env, dict))
643
639
644 def test_env_get_set_simple(self):
640 def test_env_get_set_simple(self):
645 env = _ip.magic("env var val1")
641 env = _ip.magic("env var val1")
646 self.assertEqual(env, None)
642 self.assertEqual(env, None)
647 self.assertEqual(os.environ['var'], 'val1')
643 self.assertEqual(os.environ['var'], 'val1')
648 self.assertEqual(_ip.magic("env var"), 'val1')
644 self.assertEqual(_ip.magic("env var"), 'val1')
649 env = _ip.magic("env var=val2")
645 env = _ip.magic("env var=val2")
650 self.assertEqual(env, None)
646 self.assertEqual(env, None)
651 self.assertEqual(os.environ['var'], 'val2')
647 self.assertEqual(os.environ['var'], 'val2')
652
648
653 def test_env_get_set_complex(self):
649 def test_env_get_set_complex(self):
654 env = _ip.magic("env var 'val1 '' 'val2")
650 env = _ip.magic("env var 'val1 '' 'val2")
655 self.assertEqual(env, None)
651 self.assertEqual(env, None)
656 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
652 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
657 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
653 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
658 env = _ip.magic('env var=val2 val3="val4')
654 env = _ip.magic('env var=val2 val3="val4')
659 self.assertEqual(env, None)
655 self.assertEqual(env, None)
660 self.assertEqual(os.environ['var'], 'val2 val3="val4')
656 self.assertEqual(os.environ['var'], 'val2 val3="val4')
661
657
662 def test_env_set_bad_input(self):
658 def test_env_set_bad_input(self):
663 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
659 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
664
660
665 def test_env_set_whitespace(self):
661 def test_env_set_whitespace(self):
666 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
662 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
667
663
668
664
669 class CellMagicTestCase(TestCase):
665 class CellMagicTestCase(TestCase):
670
666
671 def check_ident(self, magic):
667 def check_ident(self, magic):
672 # Manually called, we get the result
668 # Manually called, we get the result
673 out = _ip.run_cell_magic(magic, 'a', 'b')
669 out = _ip.run_cell_magic(magic, 'a', 'b')
674 nt.assert_equal(out, ('a','b'))
670 nt.assert_equal(out, ('a','b'))
675 # Via run_cell, it goes into the user's namespace via displayhook
671 # Via run_cell, it goes into the user's namespace via displayhook
676 _ip.run_cell('%%' + magic +' c\nd')
672 _ip.run_cell('%%' + magic +' c\nd')
677 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
673 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
678
674
679 def test_cell_magic_func_deco(self):
675 def test_cell_magic_func_deco(self):
680 "Cell magic using simple decorator"
676 "Cell magic using simple decorator"
681 @register_cell_magic
677 @register_cell_magic
682 def cellm(line, cell):
678 def cellm(line, cell):
683 return line, cell
679 return line, cell
684
680
685 self.check_ident('cellm')
681 self.check_ident('cellm')
686
682
687 def test_cell_magic_reg(self):
683 def test_cell_magic_reg(self):
688 "Cell magic manually registered"
684 "Cell magic manually registered"
689 def cellm(line, cell):
685 def cellm(line, cell):
690 return line, cell
686 return line, cell
691
687
692 _ip.register_magic_function(cellm, 'cell', 'cellm2')
688 _ip.register_magic_function(cellm, 'cell', 'cellm2')
693 self.check_ident('cellm2')
689 self.check_ident('cellm2')
694
690
695 def test_cell_magic_class(self):
691 def test_cell_magic_class(self):
696 "Cell magics declared via a class"
692 "Cell magics declared via a class"
697 @magics_class
693 @magics_class
698 class MyMagics(Magics):
694 class MyMagics(Magics):
699
695
700 @cell_magic
696 @cell_magic
701 def cellm3(self, line, cell):
697 def cellm3(self, line, cell):
702 return line, cell
698 return line, cell
703
699
704 _ip.register_magics(MyMagics)
700 _ip.register_magics(MyMagics)
705 self.check_ident('cellm3')
701 self.check_ident('cellm3')
706
702
707 def test_cell_magic_class2(self):
703 def test_cell_magic_class2(self):
708 "Cell magics declared via a class, #2"
704 "Cell magics declared via a class, #2"
709 @magics_class
705 @magics_class
710 class MyMagics2(Magics):
706 class MyMagics2(Magics):
711
707
712 @cell_magic('cellm4')
708 @cell_magic('cellm4')
713 def cellm33(self, line, cell):
709 def cellm33(self, line, cell):
714 return line, cell
710 return line, cell
715
711
716 _ip.register_magics(MyMagics2)
712 _ip.register_magics(MyMagics2)
717 self.check_ident('cellm4')
713 self.check_ident('cellm4')
718 # Check that nothing is registered as 'cellm33'
714 # Check that nothing is registered as 'cellm33'
719 c33 = _ip.find_cell_magic('cellm33')
715 c33 = _ip.find_cell_magic('cellm33')
720 nt.assert_equal(c33, None)
716 nt.assert_equal(c33, None)
721
717
722 def test_file():
718 def test_file():
723 """Basic %%file"""
719 """Basic %%file"""
724 ip = get_ipython()
720 ip = get_ipython()
725 with TemporaryDirectory() as td:
721 with TemporaryDirectory() as td:
726 fname = os.path.join(td, 'file1')
722 fname = os.path.join(td, 'file1')
727 ip.run_cell_magic("file", fname, u'\n'.join([
723 ip.run_cell_magic("file", fname, u'\n'.join([
728 'line1',
724 'line1',
729 'line2',
725 'line2',
730 ]))
726 ]))
731 with open(fname) as f:
727 with open(fname) as f:
732 s = f.read()
728 s = f.read()
733 nt.assert_in('line1\n', s)
729 nt.assert_in('line1\n', s)
734 nt.assert_in('line2', s)
730 nt.assert_in('line2', s)
735
731
736 def test_file_var_expand():
732 def test_file_var_expand():
737 """%%file $filename"""
733 """%%file $filename"""
738 ip = get_ipython()
734 ip = get_ipython()
739 with TemporaryDirectory() as td:
735 with TemporaryDirectory() as td:
740 fname = os.path.join(td, 'file1')
736 fname = os.path.join(td, 'file1')
741 ip.user_ns['filename'] = fname
737 ip.user_ns['filename'] = fname
742 ip.run_cell_magic("file", '$filename', u'\n'.join([
738 ip.run_cell_magic("file", '$filename', u'\n'.join([
743 'line1',
739 'line1',
744 'line2',
740 'line2',
745 ]))
741 ]))
746 with open(fname) as f:
742 with open(fname) as f:
747 s = f.read()
743 s = f.read()
748 nt.assert_in('line1\n', s)
744 nt.assert_in('line1\n', s)
749 nt.assert_in('line2', s)
745 nt.assert_in('line2', s)
750
746
751 def test_file_unicode():
747 def test_file_unicode():
752 """%%file with unicode cell"""
748 """%%file with unicode cell"""
753 ip = get_ipython()
749 ip = get_ipython()
754 with TemporaryDirectory() as td:
750 with TemporaryDirectory() as td:
755 fname = os.path.join(td, 'file1')
751 fname = os.path.join(td, 'file1')
756 ip.run_cell_magic("file", fname, u'\n'.join([
752 ip.run_cell_magic("file", fname, u'\n'.join([
757 u'liné1',
753 u'liné1',
758 u'liné2',
754 u'liné2',
759 ]))
755 ]))
760 with io.open(fname, encoding='utf-8') as f:
756 with io.open(fname, encoding='utf-8') as f:
761 s = f.read()
757 s = f.read()
762 nt.assert_in(u'liné1\n', s)
758 nt.assert_in(u'liné1\n', s)
763 nt.assert_in(u'liné2', s)
759 nt.assert_in(u'liné2', s)
764
760
765 def test_file_amend():
761 def test_file_amend():
766 """%%file -a amends files"""
762 """%%file -a amends files"""
767 ip = get_ipython()
763 ip = get_ipython()
768 with TemporaryDirectory() as td:
764 with TemporaryDirectory() as td:
769 fname = os.path.join(td, 'file2')
765 fname = os.path.join(td, 'file2')
770 ip.run_cell_magic("file", fname, u'\n'.join([
766 ip.run_cell_magic("file", fname, u'\n'.join([
771 'line1',
767 'line1',
772 'line2',
768 'line2',
773 ]))
769 ]))
774 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
770 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
775 'line3',
771 'line3',
776 'line4',
772 'line4',
777 ]))
773 ]))
778 with open(fname) as f:
774 with open(fname) as f:
779 s = f.read()
775 s = f.read()
780 nt.assert_in('line1\n', s)
776 nt.assert_in('line1\n', s)
781 nt.assert_in('line3\n', s)
777 nt.assert_in('line3\n', s)
782
778
783
779
784 def test_script_config():
780 def test_script_config():
785 ip = get_ipython()
781 ip = get_ipython()
786 ip.config.ScriptMagics.script_magics = ['whoda']
782 ip.config.ScriptMagics.script_magics = ['whoda']
787 sm = script.ScriptMagics(shell=ip)
783 sm = script.ScriptMagics(shell=ip)
788 nt.assert_in('whoda', sm.magics['cell'])
784 nt.assert_in('whoda', sm.magics['cell'])
789
785
790 @dec.skip_win32
786 @dec.skip_win32
791 def test_script_out():
787 def test_script_out():
792 ip = get_ipython()
788 ip = get_ipython()
793 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
789 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
794 nt.assert_equal(ip.user_ns['output'], 'hi\n')
790 nt.assert_equal(ip.user_ns['output'], 'hi\n')
795
791
796 @dec.skip_win32
792 @dec.skip_win32
797 def test_script_err():
793 def test_script_err():
798 ip = get_ipython()
794 ip = get_ipython()
799 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
795 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
800 nt.assert_equal(ip.user_ns['error'], 'hello\n')
796 nt.assert_equal(ip.user_ns['error'], 'hello\n')
801
797
802 @dec.skip_win32
798 @dec.skip_win32
803 def test_script_out_err():
799 def test_script_out_err():
804 ip = get_ipython()
800 ip = get_ipython()
805 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
801 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
806 nt.assert_equal(ip.user_ns['output'], 'hi\n')
802 nt.assert_equal(ip.user_ns['output'], 'hi\n')
807 nt.assert_equal(ip.user_ns['error'], 'hello\n')
803 nt.assert_equal(ip.user_ns['error'], 'hello\n')
808
804
809 @dec.skip_win32
805 @dec.skip_win32
810 def test_script_bg_out():
806 def test_script_bg_out():
811 ip = get_ipython()
807 ip = get_ipython()
812 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
808 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
813 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
809 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
814
810
815 @dec.skip_win32
811 @dec.skip_win32
816 def test_script_bg_err():
812 def test_script_bg_err():
817 ip = get_ipython()
813 ip = get_ipython()
818 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
814 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
819 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
815 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
820
816
821 @dec.skip_win32
817 @dec.skip_win32
822 def test_script_bg_out_err():
818 def test_script_bg_out_err():
823 ip = get_ipython()
819 ip = get_ipython()
824 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
820 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
825 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
821 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
826 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
822 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
827
823
828 def test_script_defaults():
824 def test_script_defaults():
829 ip = get_ipython()
825 ip = get_ipython()
830 for cmd in ['sh', 'bash', 'perl', 'ruby']:
826 for cmd in ['sh', 'bash', 'perl', 'ruby']:
831 try:
827 try:
832 find_cmd(cmd)
828 find_cmd(cmd)
833 except Exception:
829 except Exception:
834 pass
830 pass
835 else:
831 else:
836 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
832 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
837
833
838
834
839 @magics_class
835 @magics_class
840 class FooFoo(Magics):
836 class FooFoo(Magics):
841 """class with both %foo and %%foo magics"""
837 """class with both %foo and %%foo magics"""
842 @line_magic('foo')
838 @line_magic('foo')
843 def line_foo(self, line):
839 def line_foo(self, line):
844 "I am line foo"
840 "I am line foo"
845 pass
841 pass
846
842
847 @cell_magic("foo")
843 @cell_magic("foo")
848 def cell_foo(self, line, cell):
844 def cell_foo(self, line, cell):
849 "I am cell foo, not line foo"
845 "I am cell foo, not line foo"
850 pass
846 pass
851
847
852 def test_line_cell_info():
848 def test_line_cell_info():
853 """%%foo and %foo magics are distinguishable to inspect"""
849 """%%foo and %foo magics are distinguishable to inspect"""
854 ip = get_ipython()
850 ip = get_ipython()
855 ip.magics_manager.register(FooFoo)
851 ip.magics_manager.register(FooFoo)
856 oinfo = ip.object_inspect('foo')
852 oinfo = ip.object_inspect('foo')
857 nt.assert_true(oinfo['found'])
853 nt.assert_true(oinfo['found'])
858 nt.assert_true(oinfo['ismagic'])
854 nt.assert_true(oinfo['ismagic'])
859
855
860 oinfo = ip.object_inspect('%%foo')
856 oinfo = ip.object_inspect('%%foo')
861 nt.assert_true(oinfo['found'])
857 nt.assert_true(oinfo['found'])
862 nt.assert_true(oinfo['ismagic'])
858 nt.assert_true(oinfo['ismagic'])
863 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
859 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
864
860
865 oinfo = ip.object_inspect('%foo')
861 oinfo = ip.object_inspect('%foo')
866 nt.assert_true(oinfo['found'])
862 nt.assert_true(oinfo['found'])
867 nt.assert_true(oinfo['ismagic'])
863 nt.assert_true(oinfo['ismagic'])
868 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
864 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
869
865
870 def test_multiple_magics():
866 def test_multiple_magics():
871 ip = get_ipython()
867 ip = get_ipython()
872 foo1 = FooFoo(ip)
868 foo1 = FooFoo(ip)
873 foo2 = FooFoo(ip)
869 foo2 = FooFoo(ip)
874 mm = ip.magics_manager
870 mm = ip.magics_manager
875 mm.register(foo1)
871 mm.register(foo1)
876 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
872 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
877 mm.register(foo2)
873 mm.register(foo2)
878 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
874 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
879
875
880 def test_alias_magic():
876 def test_alias_magic():
881 """Test %alias_magic."""
877 """Test %alias_magic."""
882 ip = get_ipython()
878 ip = get_ipython()
883 mm = ip.magics_manager
879 mm = ip.magics_manager
884
880
885 # Basic operation: both cell and line magics are created, if possible.
881 # Basic operation: both cell and line magics are created, if possible.
886 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
882 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
887 nt.assert_in('timeit_alias', mm.magics['line'])
883 nt.assert_in('timeit_alias', mm.magics['line'])
888 nt.assert_in('timeit_alias', mm.magics['cell'])
884 nt.assert_in('timeit_alias', mm.magics['cell'])
889
885
890 # --cell is specified, line magic not created.
886 # --cell is specified, line magic not created.
891 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
887 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
892 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
888 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
893 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
889 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
894
890
895 # Test that line alias is created successfully.
891 # Test that line alias is created successfully.
896 ip.run_line_magic('alias_magic', '--line env_alias env')
892 ip.run_line_magic('alias_magic', '--line env_alias env')
897 nt.assert_equal(ip.run_line_magic('env', ''),
893 nt.assert_equal(ip.run_line_magic('env', ''),
898 ip.run_line_magic('env_alias', ''))
894 ip.run_line_magic('env_alias', ''))
899
895
900 def test_save():
896 def test_save():
901 """Test %save."""
897 """Test %save."""
902 ip = get_ipython()
898 ip = get_ipython()
903 ip.history_manager.reset() # Clear any existing history.
899 ip.history_manager.reset() # Clear any existing history.
904 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
900 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
905 for i, cmd in enumerate(cmds, start=1):
901 for i, cmd in enumerate(cmds, start=1):
906 ip.history_manager.store_inputs(i, cmd)
902 ip.history_manager.store_inputs(i, cmd)
907 with TemporaryDirectory() as tmpdir:
903 with TemporaryDirectory() as tmpdir:
908 file = os.path.join(tmpdir, "testsave.py")
904 file = os.path.join(tmpdir, "testsave.py")
909 ip.run_line_magic("save", "%s 1-10" % file)
905 ip.run_line_magic("save", "%s 1-10" % file)
910 with open(file) as f:
906 with open(file) as f:
911 content = f.read()
907 content = f.read()
912 nt.assert_equal(content.count(cmds[0]), 1)
908 nt.assert_equal(content.count(cmds[0]), 1)
913 nt.assert_in('coding: utf-8', content)
909 nt.assert_in('coding: utf-8', content)
914 ip.run_line_magic("save", "-a %s 1-10" % file)
910 ip.run_line_magic("save", "-a %s 1-10" % file)
915 with open(file) as f:
911 with open(file) as f:
916 content = f.read()
912 content = f.read()
917 nt.assert_equal(content.count(cmds[0]), 2)
913 nt.assert_equal(content.count(cmds[0]), 2)
918 nt.assert_in('coding: utf-8', content)
914 nt.assert_in('coding: utf-8', content)
919
915
920
916
921 def test_store():
917 def test_store():
922 """Test %store."""
918 """Test %store."""
923 ip = get_ipython()
919 ip = get_ipython()
924 ip.run_line_magic('load_ext', 'storemagic')
920 ip.run_line_magic('load_ext', 'storemagic')
925
921
926 # make sure the storage is empty
922 # make sure the storage is empty
927 ip.run_line_magic('store', '-z')
923 ip.run_line_magic('store', '-z')
928 ip.user_ns['var'] = 42
924 ip.user_ns['var'] = 42
929 ip.run_line_magic('store', 'var')
925 ip.run_line_magic('store', 'var')
930 ip.user_ns['var'] = 39
926 ip.user_ns['var'] = 39
931 ip.run_line_magic('store', '-r')
927 ip.run_line_magic('store', '-r')
932 nt.assert_equal(ip.user_ns['var'], 42)
928 nt.assert_equal(ip.user_ns['var'], 42)
933
929
934 ip.run_line_magic('store', '-d var')
930 ip.run_line_magic('store', '-d var')
935 ip.user_ns['var'] = 39
931 ip.user_ns['var'] = 39
936 ip.run_line_magic('store' , '-r')
932 ip.run_line_magic('store' , '-r')
937 nt.assert_equal(ip.user_ns['var'], 39)
933 nt.assert_equal(ip.user_ns['var'], 39)
938
934
939
935
940 def _run_edit_test(arg_s, exp_filename=None,
936 def _run_edit_test(arg_s, exp_filename=None,
941 exp_lineno=-1,
937 exp_lineno=-1,
942 exp_contents=None,
938 exp_contents=None,
943 exp_is_temp=None):
939 exp_is_temp=None):
944 ip = get_ipython()
940 ip = get_ipython()
945 M = code.CodeMagics(ip)
941 M = code.CodeMagics(ip)
946 last_call = ['','']
942 last_call = ['','']
947 opts,args = M.parse_options(arg_s,'prxn:')
943 opts,args = M.parse_options(arg_s,'prxn:')
948 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
944 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
949
945
950 if exp_filename is not None:
946 if exp_filename is not None:
951 nt.assert_equal(exp_filename, filename)
947 nt.assert_equal(exp_filename, filename)
952 if exp_contents is not None:
948 if exp_contents is not None:
953 with io.open(filename, 'r', encoding='utf-8') as f:
949 with io.open(filename, 'r', encoding='utf-8') as f:
954 contents = f.read()
950 contents = f.read()
955 nt.assert_equal(exp_contents, contents)
951 nt.assert_equal(exp_contents, contents)
956 if exp_lineno != -1:
952 if exp_lineno != -1:
957 nt.assert_equal(exp_lineno, lineno)
953 nt.assert_equal(exp_lineno, lineno)
958 if exp_is_temp is not None:
954 if exp_is_temp is not None:
959 nt.assert_equal(exp_is_temp, is_temp)
955 nt.assert_equal(exp_is_temp, is_temp)
960
956
961
957
962 def test_edit_interactive():
958 def test_edit_interactive():
963 """%edit on interactively defined objects"""
959 """%edit on interactively defined objects"""
964 ip = get_ipython()
960 ip = get_ipython()
965 n = ip.execution_count
961 n = ip.execution_count
966 ip.run_cell(u"def foo(): return 1", store_history=True)
962 ip.run_cell(u"def foo(): return 1", store_history=True)
967
963
968 try:
964 try:
969 _run_edit_test("foo")
965 _run_edit_test("foo")
970 except code.InteractivelyDefined as e:
966 except code.InteractivelyDefined as e:
971 nt.assert_equal(e.index, n)
967 nt.assert_equal(e.index, n)
972 else:
968 else:
973 raise AssertionError("Should have raised InteractivelyDefined")
969 raise AssertionError("Should have raised InteractivelyDefined")
974
970
975
971
976 def test_edit_cell():
972 def test_edit_cell():
977 """%edit [cell id]"""
973 """%edit [cell id]"""
978 ip = get_ipython()
974 ip = get_ipython()
979
975
980 ip.run_cell(u"def foo(): return 1", store_history=True)
976 ip.run_cell(u"def foo(): return 1", store_history=True)
981
977
982 # test
978 # test
983 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
979 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
984
980
985 def test_bookmark():
981 def test_bookmark():
986 ip = get_ipython()
982 ip = get_ipython()
987 ip.run_line_magic('bookmark', 'bmname')
983 ip.run_line_magic('bookmark', 'bmname')
988 with tt.AssertPrints('bmname'):
984 with tt.AssertPrints('bmname'):
989 ip.run_line_magic('bookmark', '-l')
985 ip.run_line_magic('bookmark', '-l')
990 ip.run_line_magic('bookmark', '-d bmname')
986 ip.run_line_magic('bookmark', '-d bmname')
991
987
992 def test_ls_magic():
988 def test_ls_magic():
993 ip = get_ipython()
989 ip = get_ipython()
994 json_formatter = ip.display_formatter.formatters['application/json']
990 json_formatter = ip.display_formatter.formatters['application/json']
995 json_formatter.enabled = True
991 json_formatter.enabled = True
996 lsmagic = ip.magic('lsmagic')
992 lsmagic = ip.magic('lsmagic')
997 with warnings.catch_warnings(record=True) as w:
993 with warnings.catch_warnings(record=True) as w:
998 j = json_formatter(lsmagic)
994 j = json_formatter(lsmagic)
999 nt.assert_equal(sorted(j), ['cell', 'line'])
995 nt.assert_equal(sorted(j), ['cell', 'line'])
1000 nt.assert_equal(w, []) # no warnings
996 nt.assert_equal(w, []) # no warnings
@@ -1,466 +1,466 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 This module defines the things that are used in setup.py for building IPython
3 This module defines the things that are used in setup.py for building IPython
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import errno
17 import errno
18 import os
18 import os
19 import sys
19 import sys
20
20
21 from distutils import log
21 from distutils import log
22 from distutils.command.build_py import build_py
22 from distutils.command.build_py import build_py
23 from distutils.command.build_scripts import build_scripts
23 from distutils.command.build_scripts import build_scripts
24 from distutils.command.install import install
24 from distutils.command.install import install
25 from distutils.command.install_scripts import install_scripts
25 from distutils.command.install_scripts import install_scripts
26 from distutils.cmd import Command
26 from distutils.cmd import Command
27 from distutils.errors import DistutilsExecError
27 from distutils.errors import DistutilsExecError
28 from fnmatch import fnmatch
28 from fnmatch import fnmatch
29 from glob import glob
29 from glob import glob
30 from subprocess import Popen, PIPE
30 from subprocess import Popen, PIPE
31
31
32 from setupext import install_data_ext
32 from setupext import install_data_ext
33
33
34 #-------------------------------------------------------------------------------
34 #-------------------------------------------------------------------------------
35 # Useful globals and utility functions
35 # Useful globals and utility functions
36 #-------------------------------------------------------------------------------
36 #-------------------------------------------------------------------------------
37
37
38 # A few handy globals
38 # A few handy globals
39 isfile = os.path.isfile
39 isfile = os.path.isfile
40 pjoin = os.path.join
40 pjoin = os.path.join
41 repo_root = os.path.dirname(os.path.abspath(__file__))
41 repo_root = os.path.dirname(os.path.abspath(__file__))
42
42
43 def oscmd(s):
43 def oscmd(s):
44 print(">", s)
44 print(">", s)
45 os.system(s)
45 os.system(s)
46
46
47 # Py3 compatibility hacks, without assuming IPython itself is installed with
47 # Py3 compatibility hacks, without assuming IPython itself is installed with
48 # the full py3compat machinery.
48 # the full py3compat machinery.
49
49
50 try:
50 try:
51 execfile
51 execfile
52 except NameError:
52 except NameError:
53 def execfile(fname, globs, locs=None):
53 def execfile(fname, globs, locs=None):
54 locs = locs or globs
54 locs = locs or globs
55 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
55 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
56
56
57 # A little utility we'll need below, since glob() does NOT allow you to do
57 # A little utility we'll need below, since glob() does NOT allow you to do
58 # exclusion on multiple endings!
58 # exclusion on multiple endings!
59 def file_doesnt_endwith(test,endings):
59 def file_doesnt_endwith(test,endings):
60 """Return true if test is a file and its name does NOT end with any
60 """Return true if test is a file and its name does NOT end with any
61 of the strings listed in endings."""
61 of the strings listed in endings."""
62 if not isfile(test):
62 if not isfile(test):
63 return False
63 return False
64 for e in endings:
64 for e in endings:
65 if test.endswith(e):
65 if test.endswith(e):
66 return False
66 return False
67 return True
67 return True
68
68
69 #---------------------------------------------------------------------------
69 #---------------------------------------------------------------------------
70 # Basic project information
70 # Basic project information
71 #---------------------------------------------------------------------------
71 #---------------------------------------------------------------------------
72
72
73 # release.py contains version, authors, license, url, keywords, etc.
73 # release.py contains version, authors, license, url, keywords, etc.
74 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
74 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
75
75
76 # Create a dict with the basic information
76 # Create a dict with the basic information
77 # This dict is eventually passed to setup after additional keys are added.
77 # This dict is eventually passed to setup after additional keys are added.
78 setup_args = dict(
78 setup_args = dict(
79 name = name,
79 name = name,
80 version = version,
80 version = version,
81 description = description,
81 description = description,
82 long_description = long_description,
82 long_description = long_description,
83 author = author,
83 author = author,
84 author_email = author_email,
84 author_email = author_email,
85 url = url,
85 url = url,
86 download_url = download_url,
86 download_url = download_url,
87 license = license,
87 license = license,
88 platforms = platforms,
88 platforms = platforms,
89 keywords = keywords,
89 keywords = keywords,
90 classifiers = classifiers,
90 classifiers = classifiers,
91 cmdclass = {'install_data': install_data_ext},
91 cmdclass = {'install_data': install_data_ext},
92 )
92 )
93
93
94
94
95 #---------------------------------------------------------------------------
95 #---------------------------------------------------------------------------
96 # Find packages
96 # Find packages
97 #---------------------------------------------------------------------------
97 #---------------------------------------------------------------------------
98
98
99 def find_packages():
99 def find_packages():
100 """
100 """
101 Find all of IPython's packages.
101 Find all of IPython's packages.
102 """
102 """
103 excludes = ['deathrow', 'quarantine']
103 excludes = ['deathrow', 'quarantine']
104 packages = []
104 packages = []
105 for dir,subdirs,files in os.walk('IPython'):
105 for dir,subdirs,files in os.walk('IPython'):
106 package = dir.replace(os.path.sep, '.')
106 package = dir.replace(os.path.sep, '.')
107 if any(package.startswith('IPython.'+exc) for exc in excludes):
107 if any(package.startswith('IPython.'+exc) for exc in excludes):
108 # package is to be excluded (e.g. deathrow)
108 # package is to be excluded (e.g. deathrow)
109 continue
109 continue
110 if '__init__.py' not in files:
110 if '__init__.py' not in files:
111 # not a package
111 # not a package
112 continue
112 continue
113 packages.append(package)
113 packages.append(package)
114 return packages
114 return packages
115
115
116 #---------------------------------------------------------------------------
116 #---------------------------------------------------------------------------
117 # Find package data
117 # Find package data
118 #---------------------------------------------------------------------------
118 #---------------------------------------------------------------------------
119
119
120 def find_package_data():
120 def find_package_data():
121 """
121 """
122 Find IPython's package_data.
122 Find IPython's package_data.
123 """
123 """
124 # This is not enough for these things to appear in an sdist.
124 # This is not enough for these things to appear in an sdist.
125 # We need to muck with the MANIFEST to get this to work
125 # We need to muck with the MANIFEST to get this to work
126
126
127 package_data = {
127 package_data = {
128 'IPython.core' : ['profile/README*'],
128 'IPython.core' : ['profile/README*'],
129 'IPython.core.tests' : ['*.png', '*.jpg'],
129 'IPython.core.tests' : ['*.png', '*.jpg', 'daft_extension/*.py'],
130 'IPython.lib.tests' : ['*.wav'],
130 'IPython.lib.tests' : ['*.wav'],
131 'IPython.testing.plugin' : ['*.txt'],
131 'IPython.testing.plugin' : ['*.txt'],
132 }
132 }
133
133
134 return package_data
134 return package_data
135
135
136
136
137 def check_package_data(package_data):
137 def check_package_data(package_data):
138 """verify that package_data globs make sense"""
138 """verify that package_data globs make sense"""
139 print("checking package data")
139 print("checking package data")
140 for pkg, data in package_data.items():
140 for pkg, data in package_data.items():
141 pkg_root = pjoin(*pkg.split('.'))
141 pkg_root = pjoin(*pkg.split('.'))
142 for d in data:
142 for d in data:
143 path = pjoin(pkg_root, d)
143 path = pjoin(pkg_root, d)
144 if '*' in path:
144 if '*' in path:
145 assert len(glob(path)) > 0, "No files match pattern %s" % path
145 assert len(glob(path)) > 0, "No files match pattern %s" % path
146 else:
146 else:
147 assert os.path.exists(path), "Missing package data: %s" % path
147 assert os.path.exists(path), "Missing package data: %s" % path
148
148
149
149
150 def check_package_data_first(command):
150 def check_package_data_first(command):
151 """decorator for checking package_data before running a given command
151 """decorator for checking package_data before running a given command
152
152
153 Probably only needs to wrap build_py
153 Probably only needs to wrap build_py
154 """
154 """
155 class DecoratedCommand(command):
155 class DecoratedCommand(command):
156 def run(self):
156 def run(self):
157 check_package_data(self.package_data)
157 check_package_data(self.package_data)
158 command.run(self)
158 command.run(self)
159 return DecoratedCommand
159 return DecoratedCommand
160
160
161
161
162 #---------------------------------------------------------------------------
162 #---------------------------------------------------------------------------
163 # Find data files
163 # Find data files
164 #---------------------------------------------------------------------------
164 #---------------------------------------------------------------------------
165
165
166 def make_dir_struct(tag,base,out_base):
166 def make_dir_struct(tag,base,out_base):
167 """Make the directory structure of all files below a starting dir.
167 """Make the directory structure of all files below a starting dir.
168
168
169 This is just a convenience routine to help build a nested directory
169 This is just a convenience routine to help build a nested directory
170 hierarchy because distutils is too stupid to do this by itself.
170 hierarchy because distutils is too stupid to do this by itself.
171
171
172 XXX - this needs a proper docstring!
172 XXX - this needs a proper docstring!
173 """
173 """
174
174
175 # we'll use these a lot below
175 # we'll use these a lot below
176 lbase = len(base)
176 lbase = len(base)
177 pathsep = os.path.sep
177 pathsep = os.path.sep
178 lpathsep = len(pathsep)
178 lpathsep = len(pathsep)
179
179
180 out = []
180 out = []
181 for (dirpath,dirnames,filenames) in os.walk(base):
181 for (dirpath,dirnames,filenames) in os.walk(base):
182 # we need to strip out the dirpath from the base to map it to the
182 # we need to strip out the dirpath from the base to map it to the
183 # output (installation) path. This requires possibly stripping the
183 # output (installation) path. This requires possibly stripping the
184 # path separator, because otherwise pjoin will not work correctly
184 # path separator, because otherwise pjoin will not work correctly
185 # (pjoin('foo/','/bar') returns '/bar').
185 # (pjoin('foo/','/bar') returns '/bar').
186
186
187 dp_eff = dirpath[lbase:]
187 dp_eff = dirpath[lbase:]
188 if dp_eff.startswith(pathsep):
188 if dp_eff.startswith(pathsep):
189 dp_eff = dp_eff[lpathsep:]
189 dp_eff = dp_eff[lpathsep:]
190 # The output path must be anchored at the out_base marker
190 # The output path must be anchored at the out_base marker
191 out_path = pjoin(out_base,dp_eff)
191 out_path = pjoin(out_base,dp_eff)
192 # Now we can generate the final filenames. Since os.walk only produces
192 # Now we can generate the final filenames. Since os.walk only produces
193 # filenames, we must join back with the dirpath to get full valid file
193 # filenames, we must join back with the dirpath to get full valid file
194 # paths:
194 # paths:
195 pfiles = [pjoin(dirpath,f) for f in filenames]
195 pfiles = [pjoin(dirpath,f) for f in filenames]
196 # Finally, generate the entry we need, which is a pari of (output
196 # Finally, generate the entry we need, which is a pari of (output
197 # path, files) for use as a data_files parameter in install_data.
197 # path, files) for use as a data_files parameter in install_data.
198 out.append((out_path, pfiles))
198 out.append((out_path, pfiles))
199
199
200 return out
200 return out
201
201
202
202
203 def find_data_files():
203 def find_data_files():
204 """
204 """
205 Find IPython's data_files.
205 Find IPython's data_files.
206
206
207 Just man pages at this point.
207 Just man pages at this point.
208 """
208 """
209
209
210 manpagebase = pjoin('share', 'man', 'man1')
210 manpagebase = pjoin('share', 'man', 'man1')
211
211
212 # Simple file lists can be made by hand
212 # Simple file lists can be made by hand
213 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
213 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
214 if not manpages:
214 if not manpages:
215 # When running from a source tree, the manpages aren't gzipped
215 # When running from a source tree, the manpages aren't gzipped
216 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
216 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
217
217
218 # And assemble the entire output list
218 # And assemble the entire output list
219 data_files = [ (manpagebase, manpages) ]
219 data_files = [ (manpagebase, manpages) ]
220
220
221 return data_files
221 return data_files
222
222
223
223
224 def make_man_update_target(manpage):
224 def make_man_update_target(manpage):
225 """Return a target_update-compliant tuple for the given manpage.
225 """Return a target_update-compliant tuple for the given manpage.
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 manpage : string
229 manpage : string
230 Name of the manpage, must include the section number (trailing number).
230 Name of the manpage, must include the section number (trailing number).
231
231
232 Example
232 Example
233 -------
233 -------
234
234
235 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
235 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
236 ('docs/man/ipython.1.gz',
236 ('docs/man/ipython.1.gz',
237 ['docs/man/ipython.1'],
237 ['docs/man/ipython.1'],
238 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
238 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
239 """
239 """
240 man_dir = pjoin('docs', 'man')
240 man_dir = pjoin('docs', 'man')
241 manpage_gz = manpage + '.gz'
241 manpage_gz = manpage + '.gz'
242 manpath = pjoin(man_dir, manpage)
242 manpath = pjoin(man_dir, manpage)
243 manpath_gz = pjoin(man_dir, manpage_gz)
243 manpath_gz = pjoin(man_dir, manpage_gz)
244 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
244 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
245 locals() )
245 locals() )
246 return (manpath_gz, [manpath], gz_cmd)
246 return (manpath_gz, [manpath], gz_cmd)
247
247
248 # The two functions below are copied from IPython.utils.path, so we don't need
248 # The two functions below are copied from IPython.utils.path, so we don't need
249 # to import IPython during setup, which fails on Python 3.
249 # to import IPython during setup, which fails on Python 3.
250
250
251 def target_outdated(target,deps):
251 def target_outdated(target,deps):
252 """Determine whether a target is out of date.
252 """Determine whether a target is out of date.
253
253
254 target_outdated(target,deps) -> 1/0
254 target_outdated(target,deps) -> 1/0
255
255
256 deps: list of filenames which MUST exist.
256 deps: list of filenames which MUST exist.
257 target: single filename which may or may not exist.
257 target: single filename which may or may not exist.
258
258
259 If target doesn't exist or is older than any file listed in deps, return
259 If target doesn't exist or is older than any file listed in deps, return
260 true, otherwise return false.
260 true, otherwise return false.
261 """
261 """
262 try:
262 try:
263 target_time = os.path.getmtime(target)
263 target_time = os.path.getmtime(target)
264 except os.error:
264 except os.error:
265 return 1
265 return 1
266 for dep in deps:
266 for dep in deps:
267 dep_time = os.path.getmtime(dep)
267 dep_time = os.path.getmtime(dep)
268 if dep_time > target_time:
268 if dep_time > target_time:
269 #print "For target",target,"Dep failed:",dep # dbg
269 #print "For target",target,"Dep failed:",dep # dbg
270 #print "times (dep,tar):",dep_time,target_time # dbg
270 #print "times (dep,tar):",dep_time,target_time # dbg
271 return 1
271 return 1
272 return 0
272 return 0
273
273
274
274
275 def target_update(target,deps,cmd):
275 def target_update(target,deps,cmd):
276 """Update a target with a given command given a list of dependencies.
276 """Update a target with a given command given a list of dependencies.
277
277
278 target_update(target,deps,cmd) -> runs cmd if target is outdated.
278 target_update(target,deps,cmd) -> runs cmd if target is outdated.
279
279
280 This is just a wrapper around target_outdated() which calls the given
280 This is just a wrapper around target_outdated() which calls the given
281 command if target is outdated."""
281 command if target is outdated."""
282
282
283 if target_outdated(target,deps):
283 if target_outdated(target,deps):
284 os.system(cmd)
284 os.system(cmd)
285
285
286 #---------------------------------------------------------------------------
286 #---------------------------------------------------------------------------
287 # Find scripts
287 # Find scripts
288 #---------------------------------------------------------------------------
288 #---------------------------------------------------------------------------
289
289
290 def find_entry_points():
290 def find_entry_points():
291 """Defines the command line entry points for IPython
291 """Defines the command line entry points for IPython
292
292
293 This always uses setuptools-style entry points. When setuptools is not in
293 This always uses setuptools-style entry points. When setuptools is not in
294 use, our own build_scripts_entrypt class below parses these and builds
294 use, our own build_scripts_entrypt class below parses these and builds
295 command line scripts.
295 command line scripts.
296
296
297 Each of our entry points gets both a plain name, e.g. ipython, and one
297 Each of our entry points gets both a plain name, e.g. ipython, and one
298 suffixed with the Python major version number, e.g. ipython3.
298 suffixed with the Python major version number, e.g. ipython3.
299 """
299 """
300 ep = [
300 ep = [
301 'ipython%s = IPython:start_ipython',
301 'ipython%s = IPython:start_ipython',
302 'iptest%s = IPython.testing.iptestcontroller:main',
302 'iptest%s = IPython.testing.iptestcontroller:main',
303 ]
303 ]
304 suffix = str(sys.version_info[0])
304 suffix = str(sys.version_info[0])
305 return [e % '' for e in ep] + [e % suffix for e in ep]
305 return [e % '' for e in ep] + [e % suffix for e in ep]
306
306
307 script_src = """#!{executable}
307 script_src = """#!{executable}
308 # This script was automatically generated by setup.py
308 # This script was automatically generated by setup.py
309 if __name__ == '__main__':
309 if __name__ == '__main__':
310 from {mod} import {func}
310 from {mod} import {func}
311 {func}()
311 {func}()
312 """
312 """
313
313
314 class build_scripts_entrypt(build_scripts):
314 class build_scripts_entrypt(build_scripts):
315 """Build the command line scripts
315 """Build the command line scripts
316
316
317 Parse setuptools style entry points and write simple scripts to run the
317 Parse setuptools style entry points and write simple scripts to run the
318 target functions.
318 target functions.
319
319
320 On Windows, this also creates .cmd wrappers for the scripts so that you can
320 On Windows, this also creates .cmd wrappers for the scripts so that you can
321 easily launch them from a command line.
321 easily launch them from a command line.
322 """
322 """
323 def run(self):
323 def run(self):
324 self.mkpath(self.build_dir)
324 self.mkpath(self.build_dir)
325 outfiles = []
325 outfiles = []
326 for script in find_entry_points():
326 for script in find_entry_points():
327 name, entrypt = script.split('=')
327 name, entrypt = script.split('=')
328 name = name.strip()
328 name = name.strip()
329 entrypt = entrypt.strip()
329 entrypt = entrypt.strip()
330 outfile = os.path.join(self.build_dir, name)
330 outfile = os.path.join(self.build_dir, name)
331 outfiles.append(outfile)
331 outfiles.append(outfile)
332 print('Writing script to', outfile)
332 print('Writing script to', outfile)
333
333
334 mod, func = entrypt.split(':')
334 mod, func = entrypt.split(':')
335 with open(outfile, 'w') as f:
335 with open(outfile, 'w') as f:
336 f.write(script_src.format(executable=sys.executable,
336 f.write(script_src.format(executable=sys.executable,
337 mod=mod, func=func))
337 mod=mod, func=func))
338
338
339 if sys.platform == 'win32':
339 if sys.platform == 'win32':
340 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
340 # Write .cmd wrappers for Windows so 'ipython' etc. work at the
341 # command line
341 # command line
342 cmd_file = os.path.join(self.build_dir, name + '.cmd')
342 cmd_file = os.path.join(self.build_dir, name + '.cmd')
343 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
343 cmd = '@"{python}" "%~dp0\{script}" %*\r\n'.format(
344 python=sys.executable, script=name)
344 python=sys.executable, script=name)
345 log.info("Writing %s wrapper script" % cmd_file)
345 log.info("Writing %s wrapper script" % cmd_file)
346 with open(cmd_file, 'w') as f:
346 with open(cmd_file, 'w') as f:
347 f.write(cmd)
347 f.write(cmd)
348
348
349 return outfiles, outfiles
349 return outfiles, outfiles
350
350
351 class install_lib_symlink(Command):
351 class install_lib_symlink(Command):
352 user_options = [
352 user_options = [
353 ('install-dir=', 'd', "directory to install to"),
353 ('install-dir=', 'd', "directory to install to"),
354 ]
354 ]
355
355
356 def initialize_options(self):
356 def initialize_options(self):
357 self.install_dir = None
357 self.install_dir = None
358
358
359 def finalize_options(self):
359 def finalize_options(self):
360 self.set_undefined_options('symlink',
360 self.set_undefined_options('symlink',
361 ('install_lib', 'install_dir'),
361 ('install_lib', 'install_dir'),
362 )
362 )
363
363
364 def run(self):
364 def run(self):
365 if sys.platform == 'win32':
365 if sys.platform == 'win32':
366 raise Exception("This doesn't work on Windows.")
366 raise Exception("This doesn't work on Windows.")
367 pkg = os.path.join(os.getcwd(), 'IPython')
367 pkg = os.path.join(os.getcwd(), 'IPython')
368 dest = os.path.join(self.install_dir, 'IPython')
368 dest = os.path.join(self.install_dir, 'IPython')
369 if os.path.islink(dest):
369 if os.path.islink(dest):
370 print('removing existing symlink at %s' % dest)
370 print('removing existing symlink at %s' % dest)
371 os.unlink(dest)
371 os.unlink(dest)
372 print('symlinking %s -> %s' % (pkg, dest))
372 print('symlinking %s -> %s' % (pkg, dest))
373 os.symlink(pkg, dest)
373 os.symlink(pkg, dest)
374
374
375 class unsymlink(install):
375 class unsymlink(install):
376 def run(self):
376 def run(self):
377 dest = os.path.join(self.install_lib, 'IPython')
377 dest = os.path.join(self.install_lib, 'IPython')
378 if os.path.islink(dest):
378 if os.path.islink(dest):
379 print('removing symlink at %s' % dest)
379 print('removing symlink at %s' % dest)
380 os.unlink(dest)
380 os.unlink(dest)
381 else:
381 else:
382 print('No symlink exists at %s' % dest)
382 print('No symlink exists at %s' % dest)
383
383
384 class install_symlinked(install):
384 class install_symlinked(install):
385 def run(self):
385 def run(self):
386 if sys.platform == 'win32':
386 if sys.platform == 'win32':
387 raise Exception("This doesn't work on Windows.")
387 raise Exception("This doesn't work on Windows.")
388
388
389 # Run all sub-commands (at least those that need to be run)
389 # Run all sub-commands (at least those that need to be run)
390 for cmd_name in self.get_sub_commands():
390 for cmd_name in self.get_sub_commands():
391 self.run_command(cmd_name)
391 self.run_command(cmd_name)
392
392
393 # 'sub_commands': a list of commands this command might have to run to
393 # 'sub_commands': a list of commands this command might have to run to
394 # get its work done. See cmd.py for more info.
394 # get its work done. See cmd.py for more info.
395 sub_commands = [('install_lib_symlink', lambda self:True),
395 sub_commands = [('install_lib_symlink', lambda self:True),
396 ('install_scripts_sym', lambda self:True),
396 ('install_scripts_sym', lambda self:True),
397 ]
397 ]
398
398
399 class install_scripts_for_symlink(install_scripts):
399 class install_scripts_for_symlink(install_scripts):
400 """Redefined to get options from 'symlink' instead of 'install'.
400 """Redefined to get options from 'symlink' instead of 'install'.
401
401
402 I love distutils almost as much as I love setuptools.
402 I love distutils almost as much as I love setuptools.
403 """
403 """
404 def finalize_options(self):
404 def finalize_options(self):
405 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
405 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
406 self.set_undefined_options('symlink',
406 self.set_undefined_options('symlink',
407 ('install_scripts', 'install_dir'),
407 ('install_scripts', 'install_dir'),
408 ('force', 'force'),
408 ('force', 'force'),
409 ('skip_build', 'skip_build'),
409 ('skip_build', 'skip_build'),
410 )
410 )
411
411
412
412
413 #---------------------------------------------------------------------------
413 #---------------------------------------------------------------------------
414 # VCS related
414 # VCS related
415 #---------------------------------------------------------------------------
415 #---------------------------------------------------------------------------
416
416
417
417
418 def git_prebuild(pkg_dir, build_cmd=build_py):
418 def git_prebuild(pkg_dir, build_cmd=build_py):
419 """Return extended build or sdist command class for recording commit
419 """Return extended build or sdist command class for recording commit
420
420
421 records git commit in IPython.utils._sysinfo.commit
421 records git commit in IPython.utils._sysinfo.commit
422
422
423 for use in IPython.utils.sysinfo.sys_info() calls after installation.
423 for use in IPython.utils.sysinfo.sys_info() calls after installation.
424 """
424 """
425
425
426 class MyBuildPy(build_cmd):
426 class MyBuildPy(build_cmd):
427 ''' Subclass to write commit data into installation tree '''
427 ''' Subclass to write commit data into installation tree '''
428 def run(self):
428 def run(self):
429 build_cmd.run(self)
429 build_cmd.run(self)
430 # this one will only fire for build commands
430 # this one will only fire for build commands
431 if hasattr(self, 'build_lib'):
431 if hasattr(self, 'build_lib'):
432 self._record_commit(self.build_lib)
432 self._record_commit(self.build_lib)
433
433
434 def make_release_tree(self, base_dir, files):
434 def make_release_tree(self, base_dir, files):
435 # this one will fire for sdist
435 # this one will fire for sdist
436 build_cmd.make_release_tree(self, base_dir, files)
436 build_cmd.make_release_tree(self, base_dir, files)
437 self._record_commit(base_dir)
437 self._record_commit(base_dir)
438
438
439 def _record_commit(self, base_dir):
439 def _record_commit(self, base_dir):
440 import subprocess
440 import subprocess
441 proc = subprocess.Popen('git rev-parse --short HEAD',
441 proc = subprocess.Popen('git rev-parse --short HEAD',
442 stdout=subprocess.PIPE,
442 stdout=subprocess.PIPE,
443 stderr=subprocess.PIPE,
443 stderr=subprocess.PIPE,
444 shell=True)
444 shell=True)
445 repo_commit, _ = proc.communicate()
445 repo_commit, _ = proc.communicate()
446 repo_commit = repo_commit.strip().decode("ascii")
446 repo_commit = repo_commit.strip().decode("ascii")
447
447
448 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
448 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
449 if os.path.isfile(out_pth) and not repo_commit:
449 if os.path.isfile(out_pth) and not repo_commit:
450 # nothing to write, don't clobber
450 # nothing to write, don't clobber
451 return
451 return
452
452
453 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
453 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
454
454
455 # remove to avoid overwriting original via hard link
455 # remove to avoid overwriting original via hard link
456 try:
456 try:
457 os.remove(out_pth)
457 os.remove(out_pth)
458 except (IOError, OSError):
458 except (IOError, OSError):
459 pass
459 pass
460 with open(out_pth, 'w') as out_file:
460 with open(out_pth, 'w') as out_file:
461 out_file.writelines([
461 out_file.writelines([
462 '# GENERATED BY setup.py\n',
462 '# GENERATED BY setup.py\n',
463 'commit = u"%s"\n' % repo_commit,
463 'commit = u"%s"\n' % repo_commit,
464 ])
464 ])
465 return MyBuildPy
465 return MyBuildPy
466
466
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now