##// END OF EJS Templates
Drop support for Python 3.3
Hugo -
Show More
@@ -1,437 +1,434 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from inspect import Signature, Parameter
8 from inspect import Signature, Parameter
9 import os
9 import os
10 import re
10 import re
11 import sys
11 import sys
12
12
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 from .. import oinspect
15 from .. import oinspect
16 from IPython.core.magic import (Magics, magics_class, line_magic,
16 from IPython.core.magic import (Magics, magics_class, line_magic,
17 cell_magic, line_cell_magic,
17 cell_magic, line_cell_magic,
18 register_line_magic, register_cell_magic,
18 register_line_magic, register_cell_magic,
19 register_line_cell_magic)
19 register_line_cell_magic)
20 from decorator import decorator
20 from decorator import decorator
21 from IPython import get_ipython
21 from IPython import get_ipython
22 from IPython.testing.tools import AssertPrints, AssertNotPrints
22 from IPython.testing.tools import AssertPrints, AssertNotPrints
23 from IPython.utils.path import compress_user
23 from IPython.utils.path import compress_user
24
24
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Globals and constants
27 # Globals and constants
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 inspector = oinspect.Inspector()
30 inspector = oinspect.Inspector()
31 ip = get_ipython()
31 ip = get_ipython()
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Local utilities
34 # Local utilities
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 # WARNING: since this test checks the line number where a function is
37 # WARNING: since this test checks the line number where a function is
38 # defined, if any code is inserted above, the following line will need to be
38 # defined, if any code is inserted above, the following line will need to be
39 # updated. Do NOT insert any whitespace between the next line and the function
39 # updated. Do NOT insert any whitespace between the next line and the function
40 # definition below.
40 # definition below.
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
42
42
43 from unittest import TestCase
43 from unittest import TestCase
44
44
45 class Test(TestCase):
45 class Test(TestCase):
46
46
47 def test_find_source_lines(self):
47 def test_find_source_lines(self):
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
49 THIS_LINE_NUMBER+6)
49 THIS_LINE_NUMBER+6)
50
50
51
51
52 # A couple of utilities to ensure these tests work the same from a source or a
52 # A couple of utilities to ensure these tests work the same from a source or a
53 # binary install
53 # binary install
54 def pyfile(fname):
54 def pyfile(fname):
55 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
55 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
56
56
57
57
58 def match_pyfiles(f1, f2):
58 def match_pyfiles(f1, f2):
59 nt.assert_equal(pyfile(f1), pyfile(f2))
59 nt.assert_equal(pyfile(f1), pyfile(f2))
60
60
61
61
62 def test_find_file():
62 def test_find_file():
63 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
63 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
64
64
65
65
66 def test_find_file_decorated1():
66 def test_find_file_decorated1():
67
67
68 @decorator
68 @decorator
69 def noop1(f):
69 def noop1(f):
70 def wrapper(*a, **kw):
70 def wrapper(*a, **kw):
71 return f(*a, **kw)
71 return f(*a, **kw)
72 return wrapper
72 return wrapper
73
73
74 @noop1
74 @noop1
75 def f(x):
75 def f(x):
76 "My docstring"
76 "My docstring"
77
77
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
79 nt.assert_equal(f.__doc__, "My docstring")
79 nt.assert_equal(f.__doc__, "My docstring")
80
80
81
81
82 def test_find_file_decorated2():
82 def test_find_file_decorated2():
83
83
84 @decorator
84 @decorator
85 def noop2(f, *a, **kw):
85 def noop2(f, *a, **kw):
86 return f(*a, **kw)
86 return f(*a, **kw)
87
87
88 @noop2
88 @noop2
89 @noop2
89 @noop2
90 @noop2
90 @noop2
91 def f(x):
91 def f(x):
92 "My docstring 2"
92 "My docstring 2"
93
93
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
95 nt.assert_equal(f.__doc__, "My docstring 2")
95 nt.assert_equal(f.__doc__, "My docstring 2")
96
96
97
97
98 def test_find_file_magic():
98 def test_find_file_magic():
99 run = ip.find_line_magic('run')
99 run = ip.find_line_magic('run')
100 nt.assert_not_equal(oinspect.find_file(run), None)
100 nt.assert_not_equal(oinspect.find_file(run), None)
101
101
102
102
103 # A few generic objects we can then inspect in the tests below
103 # A few generic objects we can then inspect in the tests below
104
104
105 class Call(object):
105 class Call(object):
106 """This is the class docstring."""
106 """This is the class docstring."""
107
107
108 def __init__(self, x, y=1):
108 def __init__(self, x, y=1):
109 """This is the constructor docstring."""
109 """This is the constructor docstring."""
110
110
111 def __call__(self, *a, **kw):
111 def __call__(self, *a, **kw):
112 """This is the call docstring."""
112 """This is the call docstring."""
113
113
114 def method(self, x, z=2):
114 def method(self, x, z=2):
115 """Some method's docstring"""
115 """Some method's docstring"""
116
116
117 class HasSignature(object):
117 class HasSignature(object):
118 """This is the class docstring."""
118 """This is the class docstring."""
119 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
119 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
120
120
121 def __init__(self, *args):
121 def __init__(self, *args):
122 """This is the init docstring"""
122 """This is the init docstring"""
123
123
124
124
125 class SimpleClass(object):
125 class SimpleClass(object):
126 def method(self, x, z=2):
126 def method(self, x, z=2):
127 """Some method's docstring"""
127 """Some method's docstring"""
128
128
129
129
130 class OldStyle:
130 class OldStyle:
131 """An old-style class for testing."""
131 """An old-style class for testing."""
132 pass
132 pass
133
133
134
134
135 def f(x, y=2, *a, **kw):
135 def f(x, y=2, *a, **kw):
136 """A simple function."""
136 """A simple function."""
137
137
138
138
139 def g(y, z=3, *a, **kw):
139 def g(y, z=3, *a, **kw):
140 pass # no docstring
140 pass # no docstring
141
141
142
142
143 @register_line_magic
143 @register_line_magic
144 def lmagic(line):
144 def lmagic(line):
145 "A line magic"
145 "A line magic"
146
146
147
147
148 @register_cell_magic
148 @register_cell_magic
149 def cmagic(line, cell):
149 def cmagic(line, cell):
150 "A cell magic"
150 "A cell magic"
151
151
152
152
153 @register_line_cell_magic
153 @register_line_cell_magic
154 def lcmagic(line, cell=None):
154 def lcmagic(line, cell=None):
155 "A line/cell magic"
155 "A line/cell magic"
156
156
157
157
158 @magics_class
158 @magics_class
159 class SimpleMagics(Magics):
159 class SimpleMagics(Magics):
160 @line_magic
160 @line_magic
161 def Clmagic(self, cline):
161 def Clmagic(self, cline):
162 "A class-based line magic"
162 "A class-based line magic"
163
163
164 @cell_magic
164 @cell_magic
165 def Ccmagic(self, cline, ccell):
165 def Ccmagic(self, cline, ccell):
166 "A class-based cell magic"
166 "A class-based cell magic"
167
167
168 @line_cell_magic
168 @line_cell_magic
169 def Clcmagic(self, cline, ccell=None):
169 def Clcmagic(self, cline, ccell=None):
170 "A class-based line/cell magic"
170 "A class-based line/cell magic"
171
171
172
172
173 class Awkward(object):
173 class Awkward(object):
174 def __getattr__(self, name):
174 def __getattr__(self, name):
175 raise Exception(name)
175 raise Exception(name)
176
176
177 class NoBoolCall:
177 class NoBoolCall:
178 """
178 """
179 callable with `__bool__` raising should still be inspect-able.
179 callable with `__bool__` raising should still be inspect-able.
180 """
180 """
181
181
182 def __call__(self):
182 def __call__(self):
183 """does nothing"""
183 """does nothing"""
184 pass
184 pass
185
185
186 def __bool__(self):
186 def __bool__(self):
187 """just raise NotImplemented"""
187 """just raise NotImplemented"""
188 raise NotImplementedError('Must be implemented')
188 raise NotImplementedError('Must be implemented')
189
189
190
190
191 class SerialLiar(object):
191 class SerialLiar(object):
192 """Attribute accesses always get another copy of the same class.
192 """Attribute accesses always get another copy of the same class.
193
193
194 unittest.mock.call does something similar, but it's not ideal for testing
194 unittest.mock.call does something similar, but it's not ideal for testing
195 as the failure mode is to eat all your RAM. This gives up after 10k levels.
195 as the failure mode is to eat all your RAM. This gives up after 10k levels.
196 """
196 """
197 def __init__(self, max_fibbing_twig, lies_told=0):
197 def __init__(self, max_fibbing_twig, lies_told=0):
198 if lies_told > 10000:
198 if lies_told > 10000:
199 raise RuntimeError('Nose too long, honesty is the best policy')
199 raise RuntimeError('Nose too long, honesty is the best policy')
200 self.max_fibbing_twig = max_fibbing_twig
200 self.max_fibbing_twig = max_fibbing_twig
201 self.lies_told = lies_told
201 self.lies_told = lies_told
202 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
202 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
203
203
204 def __getattr__(self, item):
204 def __getattr__(self, item):
205 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
205 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
206
206
207 #-----------------------------------------------------------------------------
207 #-----------------------------------------------------------------------------
208 # Tests
208 # Tests
209 #-----------------------------------------------------------------------------
209 #-----------------------------------------------------------------------------
210
210
211 def test_info():
211 def test_info():
212 "Check that Inspector.info fills out various fields as expected."
212 "Check that Inspector.info fills out various fields as expected."
213 i = inspector.info(Call, oname='Call')
213 i = inspector.info(Call, oname='Call')
214 nt.assert_equal(i['type_name'], 'type')
214 nt.assert_equal(i['type_name'], 'type')
215 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
215 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
216 nt.assert_equal(i['base_class'], expted_class)
216 nt.assert_equal(i['base_class'], expted_class)
217 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
217 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
218 fname = __file__
218 fname = __file__
219 if fname.endswith(".pyc"):
219 if fname.endswith(".pyc"):
220 fname = fname[:-1]
220 fname = fname[:-1]
221 # case-insensitive comparison needed on some filesystems
221 # case-insensitive comparison needed on some filesystems
222 # e.g. Windows:
222 # e.g. Windows:
223 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
223 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
224 nt.assert_equal(i['definition'], None)
224 nt.assert_equal(i['definition'], None)
225 nt.assert_equal(i['docstring'], Call.__doc__)
225 nt.assert_equal(i['docstring'], Call.__doc__)
226 nt.assert_equal(i['source'], None)
226 nt.assert_equal(i['source'], None)
227 nt.assert_true(i['isclass'])
227 nt.assert_true(i['isclass'])
228 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
228 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
229 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
229 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
230
230
231 i = inspector.info(Call, detail_level=1)
231 i = inspector.info(Call, detail_level=1)
232 nt.assert_not_equal(i['source'], None)
232 nt.assert_not_equal(i['source'], None)
233 nt.assert_equal(i['docstring'], None)
233 nt.assert_equal(i['docstring'], None)
234
234
235 c = Call(1)
235 c = Call(1)
236 c.__doc__ = "Modified instance docstring"
236 c.__doc__ = "Modified instance docstring"
237 i = inspector.info(c)
237 i = inspector.info(c)
238 nt.assert_equal(i['type_name'], 'Call')
238 nt.assert_equal(i['type_name'], 'Call')
239 nt.assert_equal(i['docstring'], "Modified instance docstring")
239 nt.assert_equal(i['docstring'], "Modified instance docstring")
240 nt.assert_equal(i['class_docstring'], Call.__doc__)
240 nt.assert_equal(i['class_docstring'], Call.__doc__)
241 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
241 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
242 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
242 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
243
243
244 def test_class_signature():
244 def test_class_signature():
245 info = inspector.info(HasSignature, 'HasSignature')
245 info = inspector.info(HasSignature, 'HasSignature')
246 nt.assert_equal(info['init_definition'], "HasSignature(test)")
246 nt.assert_equal(info['init_definition'], "HasSignature(test)")
247 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
247 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
248
248
249 def test_info_awkward():
249 def test_info_awkward():
250 # Just test that this doesn't throw an error.
250 # Just test that this doesn't throw an error.
251 inspector.info(Awkward())
251 inspector.info(Awkward())
252
252
253 def test_bool_raise():
253 def test_bool_raise():
254 inspector.info(NoBoolCall())
254 inspector.info(NoBoolCall())
255
255
256 def test_info_serialliar():
256 def test_info_serialliar():
257 fib_tracker = [0]
257 fib_tracker = [0]
258 inspector.info(SerialLiar(fib_tracker))
258 inspector.info(SerialLiar(fib_tracker))
259
259
260 # Nested attribute access should be cut off at 100 levels deep to avoid
260 # Nested attribute access should be cut off at 100 levels deep to avoid
261 # infinite loops: https://github.com/ipython/ipython/issues/9122
261 # infinite loops: https://github.com/ipython/ipython/issues/9122
262 nt.assert_less(fib_tracker[0], 9000)
262 nt.assert_less(fib_tracker[0], 9000)
263
263
264 def test_calldef_none():
264 def test_calldef_none():
265 # We should ignore __call__ for all of these.
265 # We should ignore __call__ for all of these.
266 for obj in [f, SimpleClass().method, any, str.upper]:
266 for obj in [f, SimpleClass().method, any, str.upper]:
267 print(obj)
267 print(obj)
268 i = inspector.info(obj)
268 i = inspector.info(obj)
269 nt.assert_is(i['call_def'], None)
269 nt.assert_is(i['call_def'], None)
270
270
271 def f_kwarg(pos, *, kwonly):
271 def f_kwarg(pos, *, kwonly):
272 pass
272 pass
273
273
274 def test_definition_kwonlyargs():
274 def test_definition_kwonlyargs():
275 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
275 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
276 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
276 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
277
277
278 def test_getdoc():
278 def test_getdoc():
279 class A(object):
279 class A(object):
280 """standard docstring"""
280 """standard docstring"""
281 pass
281 pass
282
282
283 class B(object):
283 class B(object):
284 """standard docstring"""
284 """standard docstring"""
285 def getdoc(self):
285 def getdoc(self):
286 return "custom docstring"
286 return "custom docstring"
287
287
288 class C(object):
288 class C(object):
289 """standard docstring"""
289 """standard docstring"""
290 def getdoc(self):
290 def getdoc(self):
291 return None
291 return None
292
292
293 a = A()
293 a = A()
294 b = B()
294 b = B()
295 c = C()
295 c = C()
296
296
297 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
297 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
298 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
298 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
299 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
299 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
300
300
301
301
302 def test_empty_property_has_no_source():
302 def test_empty_property_has_no_source():
303 i = inspector.info(property(), detail_level=1)
303 i = inspector.info(property(), detail_level=1)
304 nt.assert_is(i['source'], None)
304 nt.assert_is(i['source'], None)
305
305
306
306
307 def test_property_sources():
307 def test_property_sources():
308 import posixpath
308 import posixpath
309 # A simple adder whose source and signature stays
309 # A simple adder whose source and signature stays
310 # the same across Python distributions
310 # the same across Python distributions
311 def simple_add(a, b):
311 def simple_add(a, b):
312 "Adds two numbers"
312 "Adds two numbers"
313 return a + b
313 return a + b
314
314
315 class A(object):
315 class A(object):
316 @property
316 @property
317 def foo(self):
317 def foo(self):
318 return 'bar'
318 return 'bar'
319
319
320 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
320 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
321
321
322 dname = property(posixpath.dirname)
322 dname = property(posixpath.dirname)
323 adder = property(simple_add)
323 adder = property(simple_add)
324
324
325 i = inspector.info(A.foo, detail_level=1)
325 i = inspector.info(A.foo, detail_level=1)
326 nt.assert_in('def foo(self):', i['source'])
326 nt.assert_in('def foo(self):', i['source'])
327 nt.assert_in('lambda self, v:', i['source'])
327 nt.assert_in('lambda self, v:', i['source'])
328
328
329 i = inspector.info(A.dname, detail_level=1)
329 i = inspector.info(A.dname, detail_level=1)
330 nt.assert_in('def dirname(p)', i['source'])
330 nt.assert_in('def dirname(p)', i['source'])
331
331
332 i = inspector.info(A.adder, detail_level=1)
332 i = inspector.info(A.adder, detail_level=1)
333 nt.assert_in('def simple_add(a, b)', i['source'])
333 nt.assert_in('def simple_add(a, b)', i['source'])
334
334
335
335
336 def test_property_docstring_is_in_info_for_detail_level_0():
336 def test_property_docstring_is_in_info_for_detail_level_0():
337 class A(object):
337 class A(object):
338 @property
338 @property
339 def foobar(self):
339 def foobar(self):
340 """This is `foobar` property."""
340 """This is `foobar` property."""
341 pass
341 pass
342
342
343 ip.user_ns['a_obj'] = A()
343 ip.user_ns['a_obj'] = A()
344 nt.assert_equal(
344 nt.assert_equal(
345 'This is `foobar` property.',
345 'This is `foobar` property.',
346 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
346 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
347
347
348 ip.user_ns['a_cls'] = A
348 ip.user_ns['a_cls'] = A
349 nt.assert_equal(
349 nt.assert_equal(
350 'This is `foobar` property.',
350 'This is `foobar` property.',
351 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
351 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
352
352
353
353
354 def test_pdef():
354 def test_pdef():
355 # See gh-1914
355 # See gh-1914
356 def foo(): pass
356 def foo(): pass
357 inspector.pdef(foo, 'foo')
357 inspector.pdef(foo, 'foo')
358
358
359
359
360 def test_pinfo_nonascii():
360 def test_pinfo_nonascii():
361 # See gh-1177
361 # See gh-1177
362 from . import nonascii2
362 from . import nonascii2
363 ip.user_ns['nonascii2'] = nonascii2
363 ip.user_ns['nonascii2'] = nonascii2
364 ip._inspect('pinfo', 'nonascii2', detail_level=1)
364 ip._inspect('pinfo', 'nonascii2', detail_level=1)
365
365
366
366
367 def test_pinfo_docstring_no_source():
367 def test_pinfo_docstring_no_source():
368 """Docstring should be included with detail_level=1 if there is no source"""
368 """Docstring should be included with detail_level=1 if there is no source"""
369 with AssertPrints('Docstring:'):
369 with AssertPrints('Docstring:'):
370 ip._inspect('pinfo', 'str.format', detail_level=0)
370 ip._inspect('pinfo', 'str.format', detail_level=0)
371 with AssertPrints('Docstring:'):
371 with AssertPrints('Docstring:'):
372 ip._inspect('pinfo', 'str.format', detail_level=1)
372 ip._inspect('pinfo', 'str.format', detail_level=1)
373
373
374
374
375 def test_pinfo_no_docstring_if_source():
375 def test_pinfo_no_docstring_if_source():
376 """Docstring should not be included with detail_level=1 if source is found"""
376 """Docstring should not be included with detail_level=1 if source is found"""
377 def foo():
377 def foo():
378 """foo has a docstring"""
378 """foo has a docstring"""
379
379
380 ip.user_ns['foo'] = foo
380 ip.user_ns['foo'] = foo
381
381
382 with AssertPrints('Docstring:'):
382 with AssertPrints('Docstring:'):
383 ip._inspect('pinfo', 'foo', detail_level=0)
383 ip._inspect('pinfo', 'foo', detail_level=0)
384 with AssertPrints('Source:'):
384 with AssertPrints('Source:'):
385 ip._inspect('pinfo', 'foo', detail_level=1)
385 ip._inspect('pinfo', 'foo', detail_level=1)
386 with AssertNotPrints('Docstring:'):
386 with AssertNotPrints('Docstring:'):
387 ip._inspect('pinfo', 'foo', detail_level=1)
387 ip._inspect('pinfo', 'foo', detail_level=1)
388
388
389
389
390 def test_pinfo_docstring_if_detail_and_no_source():
390 def test_pinfo_docstring_if_detail_and_no_source():
391 """ Docstring should be displayed if source info not available """
391 """ Docstring should be displayed if source info not available """
392 obj_def = '''class Foo(object):
392 obj_def = '''class Foo(object):
393 """ This is a docstring for Foo """
393 """ This is a docstring for Foo """
394 def bar(self):
394 def bar(self):
395 """ This is a docstring for Foo.bar """
395 """ This is a docstring for Foo.bar """
396 pass
396 pass
397 '''
397 '''
398
398
399 ip.run_cell(obj_def)
399 ip.run_cell(obj_def)
400 ip.run_cell('foo = Foo()')
400 ip.run_cell('foo = Foo()')
401
401
402 with AssertNotPrints("Source:"):
402 with AssertNotPrints("Source:"):
403 with AssertPrints('Docstring:'):
403 with AssertPrints('Docstring:'):
404 ip._inspect('pinfo', 'foo', detail_level=0)
404 ip._inspect('pinfo', 'foo', detail_level=0)
405 with AssertPrints('Docstring:'):
405 with AssertPrints('Docstring:'):
406 ip._inspect('pinfo', 'foo', detail_level=1)
406 ip._inspect('pinfo', 'foo', detail_level=1)
407 with AssertPrints('Docstring:'):
407 with AssertPrints('Docstring:'):
408 ip._inspect('pinfo', 'foo.bar', detail_level=0)
408 ip._inspect('pinfo', 'foo.bar', detail_level=0)
409
409
410 with AssertNotPrints('Docstring:'):
410 with AssertNotPrints('Docstring:'):
411 with AssertPrints('Source:'):
411 with AssertPrints('Source:'):
412 ip._inspect('pinfo', 'foo.bar', detail_level=1)
412 ip._inspect('pinfo', 'foo.bar', detail_level=1)
413
413
414
414
415 def test_pinfo_magic():
415 def test_pinfo_magic():
416 with AssertPrints('Docstring:'):
416 with AssertPrints('Docstring:'):
417 ip._inspect('pinfo', 'lsmagic', detail_level=0)
417 ip._inspect('pinfo', 'lsmagic', detail_level=0)
418
418
419 with AssertPrints('Source:'):
419 with AssertPrints('Source:'):
420 ip._inspect('pinfo', 'lsmagic', detail_level=1)
420 ip._inspect('pinfo', 'lsmagic', detail_level=1)
421
421
422
422
423 def test_init_colors():
423 def test_init_colors():
424 # ensure colors are not present in signature info
424 # ensure colors are not present in signature info
425 info = inspector.info(HasSignature)
425 info = inspector.info(HasSignature)
426 init_def = info['init_definition']
426 init_def = info['init_definition']
427 nt.assert_not_in('[0m', init_def)
427 nt.assert_not_in('[0m', init_def)
428
428
429
429
430 def test_builtin_init():
430 def test_builtin_init():
431 info = inspector.info(list)
431 info = inspector.info(list)
432 init_def = info['init_definition']
432 init_def = info['init_definition']
433 # Python < 3.4 can't get init definition from builtins,
433 nt.assert_is_not_none(init_def)
434 # but still exercise the inspection in case of error-raising bugs.
435 if sys.version_info >= (3,4):
436 nt.assert_is_not_none(init_def)
437
434
@@ -1,373 +1,330 b''
1 """
1 """
2 This module contains factory functions that attempt
2 This module contains factory functions that attempt
3 to return Qt submodules from the various python Qt bindings.
3 to return Qt submodules from the various python Qt bindings.
4
4
5 It also protects against double-importing Qt with different
5 It also protects against double-importing Qt with different
6 bindings, which is unstable and likely to crash
6 bindings, which is unstable and likely to crash
7
7
8 This is used primarily by qt and qt_for_kernel, and shouldn't
8 This is used primarily by qt and qt_for_kernel, and shouldn't
9 be accessed directly from the outside
9 be accessed directly from the outside
10 """
10 """
11 import sys
11 import sys
12 import types
12 import types
13 from functools import partial
13 from functools import partial
14 from importlib import import_module
14 from importlib import import_module
15
15
16 from IPython.utils.version import check_version
16 from IPython.utils.version import check_version
17
17
18 # Available APIs.
18 # Available APIs.
19 QT_API_PYQT = 'pyqt' # Force version 2
19 QT_API_PYQT = 'pyqt' # Force version 2
20 QT_API_PYQT5 = 'pyqt5'
20 QT_API_PYQT5 = 'pyqt5'
21 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
21 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
22 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
22 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
23 QT_API_PYSIDE = 'pyside'
23 QT_API_PYSIDE = 'pyside'
24 QT_API_PYSIDE2 = 'pyside2'
24 QT_API_PYSIDE2 = 'pyside2'
25
25
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
27 QT_API_PYSIDE: 'PySide',
27 QT_API_PYSIDE: 'PySide',
28 QT_API_PYQT: 'PyQt4',
28 QT_API_PYQT: 'PyQt4',
29 QT_API_PYQTv1: 'PyQt4',
29 QT_API_PYQTv1: 'PyQt4',
30 QT_API_PYQT5: 'PyQt5',
30 QT_API_PYQT5: 'PyQt5',
31 QT_API_PYQT_DEFAULT: 'PyQt4',
31 QT_API_PYQT_DEFAULT: 'PyQt4',
32 }
32 }
33
33
34
34
35 class ImportDenier(object):
35 class ImportDenier(object):
36 """Import Hook that will guard against bad Qt imports
36 """Import Hook that will guard against bad Qt imports
37 once IPython commits to a specific binding
37 once IPython commits to a specific binding
38 """
38 """
39
39
40 def __init__(self):
40 def __init__(self):
41 self.__forbidden = set()
41 self.__forbidden = set()
42
42
43 def forbid(self, module_name):
43 def forbid(self, module_name):
44 sys.modules.pop(module_name, None)
44 sys.modules.pop(module_name, None)
45 self.__forbidden.add(module_name)
45 self.__forbidden.add(module_name)
46
46
47 def find_module(self, fullname, path=None):
47 def find_module(self, fullname, path=None):
48 if path:
48 if path:
49 return
49 return
50 if fullname in self.__forbidden:
50 if fullname in self.__forbidden:
51 return self
51 return self
52
52
53 def load_module(self, fullname):
53 def load_module(self, fullname):
54 raise ImportError("""
54 raise ImportError("""
55 Importing %s disabled by IPython, which has
55 Importing %s disabled by IPython, which has
56 already imported an Incompatible QT Binding: %s
56 already imported an Incompatible QT Binding: %s
57 """ % (fullname, loaded_api()))
57 """ % (fullname, loaded_api()))
58
58
59 ID = ImportDenier()
59 ID = ImportDenier()
60 sys.meta_path.insert(0, ID)
60 sys.meta_path.insert(0, ID)
61
61
62
62
63 def commit_api(api):
63 def commit_api(api):
64 """Commit to a particular API, and trigger ImportErrors on subsequent
64 """Commit to a particular API, and trigger ImportErrors on subsequent
65 dangerous imports"""
65 dangerous imports"""
66
66
67 if api == QT_API_PYSIDE2:
67 if api == QT_API_PYSIDE2:
68 ID.forbid('PySide')
68 ID.forbid('PySide')
69 ID.forbid('PyQt4')
69 ID.forbid('PyQt4')
70 ID.forbid('PyQt5')
70 ID.forbid('PyQt5')
71 if api == QT_API_PYSIDE:
71 if api == QT_API_PYSIDE:
72 ID.forbid('PySide2')
72 ID.forbid('PySide2')
73 ID.forbid('PyQt4')
73 ID.forbid('PyQt4')
74 ID.forbid('PyQt5')
74 ID.forbid('PyQt5')
75 elif api == QT_API_PYQT5:
75 elif api == QT_API_PYQT5:
76 ID.forbid('PySide2')
76 ID.forbid('PySide2')
77 ID.forbid('PySide')
77 ID.forbid('PySide')
78 ID.forbid('PyQt4')
78 ID.forbid('PyQt4')
79 else: # There are three other possibilities, all representing PyQt4
79 else: # There are three other possibilities, all representing PyQt4
80 ID.forbid('PyQt5')
80 ID.forbid('PyQt5')
81 ID.forbid('PySide2')
81 ID.forbid('PySide2')
82 ID.forbid('PySide')
82 ID.forbid('PySide')
83
83
84
84
85 def loaded_api():
85 def loaded_api():
86 """Return which API is loaded, if any
86 """Return which API is loaded, if any
87
87
88 If this returns anything besides None,
88 If this returns anything besides None,
89 importing any other Qt binding is unsafe.
89 importing any other Qt binding is unsafe.
90
90
91 Returns
91 Returns
92 -------
92 -------
93 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
93 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
94 """
94 """
95 if 'PyQt4.QtCore' in sys.modules:
95 if 'PyQt4.QtCore' in sys.modules:
96 if qtapi_version() == 2:
96 if qtapi_version() == 2:
97 return QT_API_PYQT
97 return QT_API_PYQT
98 else:
98 else:
99 return QT_API_PYQTv1
99 return QT_API_PYQTv1
100 elif 'PySide.QtCore' in sys.modules:
100 elif 'PySide.QtCore' in sys.modules:
101 return QT_API_PYSIDE
101 return QT_API_PYSIDE
102 elif 'PySide2.QtCore' in sys.modules:
102 elif 'PySide2.QtCore' in sys.modules:
103 return QT_API_PYSIDE2
103 return QT_API_PYSIDE2
104 elif 'PyQt5.QtCore' in sys.modules:
104 elif 'PyQt5.QtCore' in sys.modules:
105 return QT_API_PYQT5
105 return QT_API_PYQT5
106 return None
106 return None
107
107
108
108
109 def has_binding(api):
109 def has_binding(api):
110 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
110 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
111
111
112 Supports Python <= 3.3
113
114 Parameters
115 ----------
116 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
117 Which module to check for
118
119 Returns
120 -------
121 True if the relevant module appears to be importable
122 """
123 # we can't import an incomplete pyside and pyqt4
124 # this will cause a crash in sip (#1431)
125 # check for complete presence before importing
126 module_name = api_to_module[api]
127
128 import imp
129 try:
130 #importing top level PyQt4/PySide module is ok...
131 mod = import_module(module_name)
132 #...importing submodules is not
133 imp.find_module('QtCore', mod.__path__)
134 imp.find_module('QtGui', mod.__path__)
135 imp.find_module('QtSvg', mod.__path__)
136 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
137 # QT5 requires QtWidgets too
138 imp.find_module('QtWidgets', mod.__path__)
139
140 #we can also safely check PySide version
141 if api == QT_API_PYSIDE:
142 return check_version(mod.__version__, '1.0.3')
143 else:
144 return True
145 except ImportError:
146 return False
147
148 def has_binding_new(api):
149 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
150
151 Supports Python >= 3.4
152
153 Parameters
112 Parameters
154 ----------
113 ----------
155 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
114 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
156 Which module to check for
115 Which module to check for
157
116
158 Returns
117 Returns
159 -------
118 -------
160 True if the relevant module appears to be importable
119 True if the relevant module appears to be importable
161 """
120 """
162 module_name = api_to_module[api]
121 module_name = api_to_module[api]
163 from importlib.util import find_spec
122 from importlib.util import find_spec
164
123
165 required = ['QtCore', 'QtGui', 'QtSvg']
124 required = ['QtCore', 'QtGui', 'QtSvg']
166 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
125 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
167 # QT5 requires QtWidgets too
126 # QT5 requires QtWidgets too
168 required.append('QtWidgets')
127 required.append('QtWidgets')
169
128
170 for submod in required:
129 for submod in required:
171 try:
130 try:
172 spec = find_spec('%s.%s' % (module_name, submod))
131 spec = find_spec('%s.%s' % (module_name, submod))
173 except ImportError:
132 except ImportError:
174 # Package (e.g. PyQt5) not found
133 # Package (e.g. PyQt5) not found
175 return False
134 return False
176 else:
135 else:
177 if spec is None:
136 if spec is None:
178 # Submodule (e.g. PyQt5.QtCore) not found
137 # Submodule (e.g. PyQt5.QtCore) not found
179 return False
138 return False
180
139
181 if api == QT_API_PYSIDE:
140 if api == QT_API_PYSIDE:
182 # We can also safely check PySide version
141 # We can also safely check PySide version
183 import PySide
142 import PySide
184 return check_version(PySide.__version__, '1.0.3')
143 return check_version(PySide.__version__, '1.0.3')
185
144
186 return True
145 return True
187
146
188 if sys.version_info >= (3, 4):
189 has_binding = has_binding_new
190
147
191 def qtapi_version():
148 def qtapi_version():
192 """Return which QString API has been set, if any
149 """Return which QString API has been set, if any
193
150
194 Returns
151 Returns
195 -------
152 -------
196 The QString API version (1 or 2), or None if not set
153 The QString API version (1 or 2), or None if not set
197 """
154 """
198 try:
155 try:
199 import sip
156 import sip
200 except ImportError:
157 except ImportError:
201 return
158 return
202 try:
159 try:
203 return sip.getapi('QString')
160 return sip.getapi('QString')
204 except ValueError:
161 except ValueError:
205 return
162 return
206
163
207
164
208 def can_import(api):
165 def can_import(api):
209 """Safely query whether an API is importable, without importing it"""
166 """Safely query whether an API is importable, without importing it"""
210 if not has_binding(api):
167 if not has_binding(api):
211 return False
168 return False
212
169
213 current = loaded_api()
170 current = loaded_api()
214 if api == QT_API_PYQT_DEFAULT:
171 if api == QT_API_PYQT_DEFAULT:
215 return current in [QT_API_PYQT, QT_API_PYQTv1, None]
172 return current in [QT_API_PYQT, QT_API_PYQTv1, None]
216 else:
173 else:
217 return current in [api, None]
174 return current in [api, None]
218
175
219
176
220 def import_pyqt4(version=2):
177 def import_pyqt4(version=2):
221 """
178 """
222 Import PyQt4
179 Import PyQt4
223
180
224 Parameters
181 Parameters
225 ----------
182 ----------
226 version : 1, 2, or None
183 version : 1, 2, or None
227 Which QString/QVariant API to use. Set to None to use the system
184 Which QString/QVariant API to use. Set to None to use the system
228 default
185 default
229
186
230 ImportErrors rasied within this function are non-recoverable
187 ImportErrors rasied within this function are non-recoverable
231 """
188 """
232 # The new-style string API (version=2) automatically
189 # The new-style string API (version=2) automatically
233 # converts QStrings to Unicode Python strings. Also, automatically unpacks
190 # converts QStrings to Unicode Python strings. Also, automatically unpacks
234 # QVariants to their underlying objects.
191 # QVariants to their underlying objects.
235 import sip
192 import sip
236
193
237 if version is not None:
194 if version is not None:
238 sip.setapi('QString', version)
195 sip.setapi('QString', version)
239 sip.setapi('QVariant', version)
196 sip.setapi('QVariant', version)
240
197
241 from PyQt4 import QtGui, QtCore, QtSvg
198 from PyQt4 import QtGui, QtCore, QtSvg
242
199
243 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
200 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
244 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
201 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
245 QtCore.PYQT_VERSION_STR)
202 QtCore.PYQT_VERSION_STR)
246
203
247 # Alias PyQt-specific functions for PySide compatibility.
204 # Alias PyQt-specific functions for PySide compatibility.
248 QtCore.Signal = QtCore.pyqtSignal
205 QtCore.Signal = QtCore.pyqtSignal
249 QtCore.Slot = QtCore.pyqtSlot
206 QtCore.Slot = QtCore.pyqtSlot
250
207
251 # query for the API version (in case version == None)
208 # query for the API version (in case version == None)
252 version = sip.getapi('QString')
209 version = sip.getapi('QString')
253 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
210 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
254 return QtCore, QtGui, QtSvg, api
211 return QtCore, QtGui, QtSvg, api
255
212
256
213
257 def import_pyqt5():
214 def import_pyqt5():
258 """
215 """
259 Import PyQt5
216 Import PyQt5
260
217
261 ImportErrors rasied within this function are non-recoverable
218 ImportErrors rasied within this function are non-recoverable
262 """
219 """
263 import sip
220 import sip
264
221
265 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
222 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
266
223
267 # Alias PyQt-specific functions for PySide compatibility.
224 # Alias PyQt-specific functions for PySide compatibility.
268 QtCore.Signal = QtCore.pyqtSignal
225 QtCore.Signal = QtCore.pyqtSignal
269 QtCore.Slot = QtCore.pyqtSlot
226 QtCore.Slot = QtCore.pyqtSlot
270
227
271 # Join QtGui and QtWidgets for Qt4 compatibility.
228 # Join QtGui and QtWidgets for Qt4 compatibility.
272 QtGuiCompat = types.ModuleType('QtGuiCompat')
229 QtGuiCompat = types.ModuleType('QtGuiCompat')
273 QtGuiCompat.__dict__.update(QtGui.__dict__)
230 QtGuiCompat.__dict__.update(QtGui.__dict__)
274 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
231 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
275
232
276 api = QT_API_PYQT5
233 api = QT_API_PYQT5
277 return QtCore, QtGuiCompat, QtSvg, api
234 return QtCore, QtGuiCompat, QtSvg, api
278
235
279
236
280 def import_pyside():
237 def import_pyside():
281 """
238 """
282 Import PySide
239 Import PySide
283
240
284 ImportErrors raised within this function are non-recoverable
241 ImportErrors raised within this function are non-recoverable
285 """
242 """
286 from PySide import QtGui, QtCore, QtSvg
243 from PySide import QtGui, QtCore, QtSvg
287 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
244 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
288
245
289 def import_pyside2():
246 def import_pyside2():
290 """
247 """
291 Import PySide2
248 Import PySide2
292
249
293 ImportErrors raised within this function are non-recoverable
250 ImportErrors raised within this function are non-recoverable
294 """
251 """
295 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
252 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
296
253
297 # Join QtGui and QtWidgets for Qt4 compatibility.
254 # Join QtGui and QtWidgets for Qt4 compatibility.
298 QtGuiCompat = types.ModuleType('QtGuiCompat')
255 QtGuiCompat = types.ModuleType('QtGuiCompat')
299 QtGuiCompat.__dict__.update(QtGui.__dict__)
256 QtGuiCompat.__dict__.update(QtGui.__dict__)
300 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
257 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
301 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
258 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
302
259
303 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
260 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
304
261
305
262
306 def load_qt(api_options):
263 def load_qt(api_options):
307 """
264 """
308 Attempt to import Qt, given a preference list
265 Attempt to import Qt, given a preference list
309 of permissible bindings
266 of permissible bindings
310
267
311 It is safe to call this function multiple times.
268 It is safe to call this function multiple times.
312
269
313 Parameters
270 Parameters
314 ----------
271 ----------
315 api_options: List of strings
272 api_options: List of strings
316 The order of APIs to try. Valid items are 'pyside', 'pyside2',
273 The order of APIs to try. Valid items are 'pyside', 'pyside2',
317 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
274 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
318
275
319 Returns
276 Returns
320 -------
277 -------
321
278
322 A tuple of QtCore, QtGui, QtSvg, QT_API
279 A tuple of QtCore, QtGui, QtSvg, QT_API
323 The first three are the Qt modules. The last is the
280 The first three are the Qt modules. The last is the
324 string indicating which module was loaded.
281 string indicating which module was loaded.
325
282
326 Raises
283 Raises
327 ------
284 ------
328 ImportError, if it isn't possible to import any requested
285 ImportError, if it isn't possible to import any requested
329 bindings (either becaues they aren't installed, or because
286 bindings (either becaues they aren't installed, or because
330 an incompatible library has already been installed)
287 an incompatible library has already been installed)
331 """
288 """
332 loaders = {
289 loaders = {
333 QT_API_PYSIDE2: import_pyside2,
290 QT_API_PYSIDE2: import_pyside2,
334 QT_API_PYSIDE: import_pyside,
291 QT_API_PYSIDE: import_pyside,
335 QT_API_PYQT: import_pyqt4,
292 QT_API_PYQT: import_pyqt4,
336 QT_API_PYQT5: import_pyqt5,
293 QT_API_PYQT5: import_pyqt5,
337 QT_API_PYQTv1: partial(import_pyqt4, version=1),
294 QT_API_PYQTv1: partial(import_pyqt4, version=1),
338 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
295 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
339 }
296 }
340
297
341 for api in api_options:
298 for api in api_options:
342
299
343 if api not in loaders:
300 if api not in loaders:
344 raise RuntimeError(
301 raise RuntimeError(
345 "Invalid Qt API %r, valid values are: %s" %
302 "Invalid Qt API %r, valid values are: %s" %
346 (api, ", ".join(["%r" % k for k in loaders.keys()])))
303 (api, ", ".join(["%r" % k for k in loaders.keys()])))
347
304
348 if not can_import(api):
305 if not can_import(api):
349 continue
306 continue
350
307
351 #cannot safely recover from an ImportError during this
308 #cannot safely recover from an ImportError during this
352 result = loaders[api]()
309 result = loaders[api]()
353 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
310 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
354 commit_api(api)
311 commit_api(api)
355 return result
312 return result
356 else:
313 else:
357 raise ImportError("""
314 raise ImportError("""
358 Could not load requested Qt binding. Please ensure that
315 Could not load requested Qt binding. Please ensure that
359 PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available,
316 PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available,
360 and only one is imported per session.
317 and only one is imported per session.
361
318
362 Currently-imported Qt library: %r
319 Currently-imported Qt library: %r
363 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
320 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
364 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
321 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
365 PySide >= 1.0.3 installed: %s
322 PySide >= 1.0.3 installed: %s
366 PySide2 installed: %s
323 PySide2 installed: %s
367 Tried to load: %r
324 Tried to load: %r
368 """ % (loaded_api(),
325 """ % (loaded_api(),
369 has_binding(QT_API_PYQT),
326 has_binding(QT_API_PYQT),
370 has_binding(QT_API_PYQT5),
327 has_binding(QT_API_PYQT5),
371 has_binding(QT_API_PYSIDE),
328 has_binding(QT_API_PYSIDE),
372 has_binding(QT_API_PYSIDE2),
329 has_binding(QT_API_PYSIDE2),
373 api_options))
330 api_options))
@@ -1,217 +1,213 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.text"""
2 """Tests for IPython.utils.text"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2011 The IPython Development Team
5 # Copyright (C) 2011 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import math
16 import math
17 import random
17 import random
18 import sys
18 import sys
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21 from pathlib import Path
21 from pathlib import Path
22
22
23 from IPython.utils import text
23 from IPython.utils import text
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Globals
26 # Globals
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 def test_columnize():
29 def test_columnize():
30 """Basic columnize tests."""
30 """Basic columnize tests."""
31 size = 5
31 size = 5
32 items = [l*size for l in 'abcd']
32 items = [l*size for l in 'abcd']
33
33
34 out = text.columnize(items, displaywidth=80)
34 out = text.columnize(items, displaywidth=80)
35 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
35 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
36 out = text.columnize(items, displaywidth=25)
36 out = text.columnize(items, displaywidth=25)
37 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
37 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
38 out = text.columnize(items, displaywidth=12)
38 out = text.columnize(items, displaywidth=12)
39 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
39 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
40 out = text.columnize(items, displaywidth=10)
40 out = text.columnize(items, displaywidth=10)
41 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
41 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
42
42
43 out = text.columnize(items, row_first=True, displaywidth=80)
43 out = text.columnize(items, row_first=True, displaywidth=80)
44 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
44 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
45 out = text.columnize(items, row_first=True, displaywidth=25)
45 out = text.columnize(items, row_first=True, displaywidth=25)
46 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
46 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
47 out = text.columnize(items, row_first=True, displaywidth=12)
47 out = text.columnize(items, row_first=True, displaywidth=12)
48 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
48 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
49 out = text.columnize(items, row_first=True, displaywidth=10)
49 out = text.columnize(items, row_first=True, displaywidth=10)
50 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
50 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
51
51
52 out = text.columnize(items, displaywidth=40, spread=True)
52 out = text.columnize(items, displaywidth=40, spread=True)
53 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
53 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
54 out = text.columnize(items, displaywidth=20, spread=True)
54 out = text.columnize(items, displaywidth=20, spread=True)
55 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
55 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
56 out = text.columnize(items, displaywidth=12, spread=True)
56 out = text.columnize(items, displaywidth=12, spread=True)
57 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
57 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
58 out = text.columnize(items, displaywidth=10, spread=True)
58 out = text.columnize(items, displaywidth=10, spread=True)
59 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
59 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
60
60
61
61
62 def test_columnize_random():
62 def test_columnize_random():
63 """Test with random input to hopefully catch edge case """
63 """Test with random input to hopefully catch edge case """
64 for row_first in [True, False]:
64 for row_first in [True, False]:
65 for nitems in [random.randint(2,70) for i in range(2,20)]:
65 for nitems in [random.randint(2,70) for i in range(2,20)]:
66 displaywidth = random.randint(20,200)
66 displaywidth = random.randint(20,200)
67 rand_len = [random.randint(2,displaywidth) for i in range(nitems)]
67 rand_len = [random.randint(2,displaywidth) for i in range(nitems)]
68 items = ['x'*l for l in rand_len]
68 items = ['x'*l for l in rand_len]
69 out = text.columnize(items, row_first=row_first, displaywidth=displaywidth)
69 out = text.columnize(items, row_first=row_first, displaywidth=displaywidth)
70 longer_line = max([len(x) for x in out.split('\n')])
70 longer_line = max([len(x) for x in out.split('\n')])
71 longer_element = max(rand_len)
71 longer_element = max(rand_len)
72 if longer_line > displaywidth:
72 if longer_line > displaywidth:
73 print("Columnize displayed something lager than displaywidth : %s " % longer_line)
73 print("Columnize displayed something lager than displaywidth : %s " % longer_line)
74 print("longer element : %s " % longer_element)
74 print("longer element : %s " % longer_element)
75 print("displaywidth : %s " % displaywidth)
75 print("displaywidth : %s " % displaywidth)
76 print("number of element : %s " % nitems)
76 print("number of element : %s " % nitems)
77 print("size of each element :\n %s" % rand_len)
77 print("size of each element :\n %s" % rand_len)
78 assert False, "row_first={0}".format(row_first)
78 assert False, "row_first={0}".format(row_first)
79
79
80 def test_columnize_medium():
80 def test_columnize_medium():
81 """Test with inputs than shouldn't be wider than 80"""
81 """Test with inputs than shouldn't be wider than 80"""
82 size = 40
82 size = 40
83 items = [l*size for l in 'abc']
83 items = [l*size for l in 'abc']
84 for row_first in [True, False]:
84 for row_first in [True, False]:
85 out = text.columnize(items, row_first=row_first, displaywidth=80)
85 out = text.columnize(items, row_first=row_first, displaywidth=80)
86 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
86 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
87
87
88 def test_columnize_long():
88 def test_columnize_long():
89 """Test columnize with inputs longer than the display window"""
89 """Test columnize with inputs longer than the display window"""
90 size = 11
90 size = 11
91 items = [l*size for l in 'abc']
91 items = [l*size for l in 'abc']
92 for row_first in [True, False]:
92 for row_first in [True, False]:
93 out = text.columnize(items, row_first=row_first, displaywidth=size-1)
93 out = text.columnize(items, row_first=row_first, displaywidth=size-1)
94 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
94 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
95
95
96 def eval_formatter_check(f):
96 def eval_formatter_check(f):
97 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"cafΓ©", b="cafΓ©")
97 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"cafΓ©", b="cafΓ©")
98 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
98 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
99 nt.assert_equal(s, "12 3 hello")
99 nt.assert_equal(s, "12 3 hello")
100 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
100 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
101 nt.assert_equal(s, "12 6 4 3 2 2 1")
101 nt.assert_equal(s, "12 6 4 3 2 2 1")
102 s = f.format('{[n//i for i in range(1,8)]}', **ns)
102 s = f.format('{[n//i for i in range(1,8)]}', **ns)
103 nt.assert_equal(s, "[12, 6, 4, 3, 2, 2, 1]")
103 nt.assert_equal(s, "[12, 6, 4, 3, 2, 2, 1]")
104 s = f.format("{stuff!s}", **ns)
104 s = f.format("{stuff!s}", **ns)
105 nt.assert_equal(s, ns['stuff'])
105 nt.assert_equal(s, ns['stuff'])
106 s = f.format("{stuff!r}", **ns)
106 s = f.format("{stuff!r}", **ns)
107 nt.assert_equal(s, repr(ns['stuff']))
107 nt.assert_equal(s, repr(ns['stuff']))
108
108
109 # Check with unicode:
109 # Check with unicode:
110 s = f.format("{u}", **ns)
110 s = f.format("{u}", **ns)
111 nt.assert_equal(s, ns['u'])
111 nt.assert_equal(s, ns['u'])
112 # This decodes in a platform dependent manner, but it shouldn't error out
112 # This decodes in a platform dependent manner, but it shouldn't error out
113 s = f.format("{b}", **ns)
113 s = f.format("{b}", **ns)
114
114
115 nt.assert_raises(NameError, f.format, '{dne}', **ns)
115 nt.assert_raises(NameError, f.format, '{dne}', **ns)
116
116
117 def eval_formatter_slicing_check(f):
117 def eval_formatter_slicing_check(f):
118 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
118 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
119 s = f.format(" {stuff.split()[:]} ", **ns)
119 s = f.format(" {stuff.split()[:]} ", **ns)
120 nt.assert_equal(s, " ['hello', 'there'] ")
120 nt.assert_equal(s, " ['hello', 'there'] ")
121 s = f.format(" {stuff.split()[::-1]} ", **ns)
121 s = f.format(" {stuff.split()[::-1]} ", **ns)
122 nt.assert_equal(s, " ['there', 'hello'] ")
122 nt.assert_equal(s, " ['there', 'hello'] ")
123 s = f.format("{stuff[::2]}", **ns)
123 s = f.format("{stuff[::2]}", **ns)
124 nt.assert_equal(s, ns['stuff'][::2])
124 nt.assert_equal(s, ns['stuff'][::2])
125
125
126 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
126 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
127
127
128 def eval_formatter_no_slicing_check(f):
128 def eval_formatter_no_slicing_check(f):
129 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
129 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
130
130
131 s = f.format('{n:x} {pi**2:+f}', **ns)
131 s = f.format('{n:x} {pi**2:+f}', **ns)
132 nt.assert_equal(s, "c +9.869604")
132 nt.assert_equal(s, "c +9.869604")
133
133
134 s = f.format('{stuff[slice(1,4)]}', **ns)
134 s = f.format('{stuff[slice(1,4)]}', **ns)
135 nt.assert_equal(s, 'ell')
135 nt.assert_equal(s, 'ell')
136
136
137 if sys.version_info >= (3, 4):
137 s = f.format("{a[:]}", a=[1, 2])
138 # String formatting has changed in Python 3.4, so this now works.
138 nt.assert_equal(s, "[1, 2]")
139 s = f.format("{a[:]}", a=[1, 2])
140 nt.assert_equal(s, "[1, 2]")
141 else:
142 nt.assert_raises(SyntaxError, f.format, "{a[:]}")
143
139
144 def test_eval_formatter():
140 def test_eval_formatter():
145 f = text.EvalFormatter()
141 f = text.EvalFormatter()
146 eval_formatter_check(f)
142 eval_formatter_check(f)
147 eval_formatter_no_slicing_check(f)
143 eval_formatter_no_slicing_check(f)
148
144
149 def test_full_eval_formatter():
145 def test_full_eval_formatter():
150 f = text.FullEvalFormatter()
146 f = text.FullEvalFormatter()
151 eval_formatter_check(f)
147 eval_formatter_check(f)
152 eval_formatter_slicing_check(f)
148 eval_formatter_slicing_check(f)
153
149
154 def test_dollar_formatter():
150 def test_dollar_formatter():
155 f = text.DollarFormatter()
151 f = text.DollarFormatter()
156 eval_formatter_check(f)
152 eval_formatter_check(f)
157 eval_formatter_slicing_check(f)
153 eval_formatter_slicing_check(f)
158
154
159 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
155 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
160 s = f.format("$n", **ns)
156 s = f.format("$n", **ns)
161 nt.assert_equal(s, "12")
157 nt.assert_equal(s, "12")
162 s = f.format("$n.real", **ns)
158 s = f.format("$n.real", **ns)
163 nt.assert_equal(s, "12")
159 nt.assert_equal(s, "12")
164 s = f.format("$n/{stuff[:5]}", **ns)
160 s = f.format("$n/{stuff[:5]}", **ns)
165 nt.assert_equal(s, "12/hello")
161 nt.assert_equal(s, "12/hello")
166 s = f.format("$n $$HOME", **ns)
162 s = f.format("$n $$HOME", **ns)
167 nt.assert_equal(s, "12 $HOME")
163 nt.assert_equal(s, "12 $HOME")
168 s = f.format("${foo}", foo="HOME")
164 s = f.format("${foo}", foo="HOME")
169 nt.assert_equal(s, "$HOME")
165 nt.assert_equal(s, "$HOME")
170
166
171
167
172 def test_long_substr():
168 def test_long_substr():
173 data = ['hi']
169 data = ['hi']
174 nt.assert_equal(text.long_substr(data), 'hi')
170 nt.assert_equal(text.long_substr(data), 'hi')
175
171
176
172
177 def test_long_substr2():
173 def test_long_substr2():
178 data = ['abc', 'abd', 'abf', 'ab']
174 data = ['abc', 'abd', 'abf', 'ab']
179 nt.assert_equal(text.long_substr(data), 'ab')
175 nt.assert_equal(text.long_substr(data), 'ab')
180
176
181 def test_long_substr_empty():
177 def test_long_substr_empty():
182 data = []
178 data = []
183 nt.assert_equal(text.long_substr(data), '')
179 nt.assert_equal(text.long_substr(data), '')
184
180
185 def test_strip_email():
181 def test_strip_email():
186 src = """\
182 src = """\
187 >> >>> def f(x):
183 >> >>> def f(x):
188 >> ... return x+1
184 >> ... return x+1
189 >> ...
185 >> ...
190 >> >>> zz = f(2.5)"""
186 >> >>> zz = f(2.5)"""
191 cln = """\
187 cln = """\
192 >>> def f(x):
188 >>> def f(x):
193 ... return x+1
189 ... return x+1
194 ...
190 ...
195 >>> zz = f(2.5)"""
191 >>> zz = f(2.5)"""
196 nt.assert_equal(text.strip_email_quotes(src), cln)
192 nt.assert_equal(text.strip_email_quotes(src), cln)
197
193
198
194
199 def test_strip_email2():
195 def test_strip_email2():
200 src = '> > > list()'
196 src = '> > > list()'
201 cln = 'list()'
197 cln = 'list()'
202 nt.assert_equal(text.strip_email_quotes(src), cln)
198 nt.assert_equal(text.strip_email_quotes(src), cln)
203
199
204 def test_LSString():
200 def test_LSString():
205 lss = text.LSString("abc\ndef")
201 lss = text.LSString("abc\ndef")
206 nt.assert_equal(lss.l, ['abc', 'def'])
202 nt.assert_equal(lss.l, ['abc', 'def'])
207 nt.assert_equal(lss.s, 'abc def')
203 nt.assert_equal(lss.s, 'abc def')
208 lss = text.LSString(os.getcwd())
204 lss = text.LSString(os.getcwd())
209 nt.assert_is_instance(lss.p[0], Path)
205 nt.assert_is_instance(lss.p[0], Path)
210
206
211 def test_SList():
207 def test_SList():
212 sl = text.SList(['a 11', 'b 1', 'a 2'])
208 sl = text.SList(['a 11', 'b 1', 'a 2'])
213 nt.assert_equal(sl.n, 'a 11\nb 1\na 2')
209 nt.assert_equal(sl.n, 'a 11\nb 1\na 2')
214 nt.assert_equal(sl.s, 'a 11 b 1 a 2')
210 nt.assert_equal(sl.s, 'a 11 b 1 a 2')
215 nt.assert_equal(sl.grep(lambda x: x.startswith('a')), text.SList(['a 11', 'a 2']))
211 nt.assert_equal(sl.grep(lambda x: x.startswith('a')), text.SList(['a 11', 'a 2']))
216 nt.assert_equal(sl.fields(0), text.SList(['a', 'b', 'a']))
212 nt.assert_equal(sl.fields(0), text.SList(['a', 'b', 'a']))
217 nt.assert_equal(sl.sort(field=1, nums=True), text.SList(['b 1', 'a 2', 'a 11']))
213 nt.assert_equal(sl.sort(field=1, nums=True), text.SList(['b 1', 'a 2', 'a 11']))
@@ -1,109 +1,111 b''
1 .. image:: https://codecov.io/github/ipython/ipython/coverage.svg?branch=master
1 .. image:: https://codecov.io/github/ipython/ipython/coverage.svg?branch=master
2 :target: https://codecov.io/github/ipython/ipython?branch=master
2 :target: https://codecov.io/github/ipython/ipython?branch=master
3
3
4 .. image:: https://img.shields.io/pypi/v/IPython.svg
4 .. image:: https://img.shields.io/pypi/v/IPython.svg
5 :target: https://pypi.python.org/pypi/ipython
5 :target: https://pypi.python.org/pypi/ipython
6
6
7 .. image:: https://img.shields.io/travis/ipython/ipython.svg
7 .. image:: https://img.shields.io/travis/ipython/ipython.svg
8 :target: https://travis-ci.org/ipython/ipython
8 :target: https://travis-ci.org/ipython/ipython
9
9
10 .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg
10 .. image:: https://www.codetriage.com/ipython/ipython/badges/users.svg
11 :target: https://www.codetriage.com/ipython/ipython/
11 :target: https://www.codetriage.com/ipython/ipython/
12
12
13 ===========================================
13 ===========================================
14 IPython: Productive Interactive Computing
14 IPython: Productive Interactive Computing
15 ===========================================
15 ===========================================
16
16
17 Overview
17 Overview
18 ========
18 ========
19
19
20 Welcome to IPython. Our full documentation is available on `ipython.readthedocs.io
20 Welcome to IPython. Our full documentation is available on `ipython.readthedocs.io
21 <https://ipython.readthedocs.io/en/stable/>`_ and contains information on how to install, use and
21 <https://ipython.readthedocs.io/en/stable/>`_ and contains information on how to install, use and
22 contribute to the project.
22 contribute to the project.
23
23
24 **IPython versions and Python Support**
24 **IPython versions and Python Support**
25
25
26 **IPython 6** requires Python version 3.3 and above.
26 **IPython 6.3** requires Python version 3.4 and above.
27
28 **IPython 6.0-6.2** requires Python version 3.3 and above.
27
29
28 **IPython 5.x LTS** is the compatible release for Python 2.7.
30 **IPython 5.x LTS** is the compatible release for Python 2.7.
29 If you require Python 2 support, you **must** use IPython 5.x LTS. Please
31 If you require Python 2 support, you **must** use IPython 5.x LTS. Please
30 update your project configurations and requirements as necessary.
32 update your project configurations and requirements as necessary.
31
33
32
34
33 The Notebook, Qt console and a number of other pieces are now parts of *Jupyter*.
35 The Notebook, Qt console and a number of other pieces are now parts of *Jupyter*.
34 See the `Jupyter installation docs <http://jupyter.readthedocs.io/en/latest/install.html>`__
36 See the `Jupyter installation docs <http://jupyter.readthedocs.io/en/latest/install.html>`__
35 if you want to use these.
37 if you want to use these.
36
38
37
39
38
40
39
41
40 Development and Instant running
42 Development and Instant running
41 ===============================
43 ===============================
42
44
43 You can find the latest version of the development documentation on `readthedocs
45 You can find the latest version of the development documentation on `readthedocs
44 <http://ipython.readthedocs.io/en/latest/>`_.
46 <http://ipython.readthedocs.io/en/latest/>`_.
45
47
46 You can run IPython from this directory without even installing it system-wide
48 You can run IPython from this directory without even installing it system-wide
47 by typing at the terminal::
49 by typing at the terminal::
48
50
49 $ python -m IPython
51 $ python -m IPython
50
52
51 Or see the `development installation docs
53 Or see the `development installation docs
52 <http://ipython.readthedocs.io/en/latest/install/install.html#installing-the-development-version>`_
54 <http://ipython.readthedocs.io/en/latest/install/install.html#installing-the-development-version>`_
53 for the latest revision on read the docs.
55 for the latest revision on read the docs.
54
56
55 Documentation and installation instructions for older version of IPython can be
57 Documentation and installation instructions for older version of IPython can be
56 found on the `IPython website <http://ipython.org/documentation.html>`_
58 found on the `IPython website <http://ipython.org/documentation.html>`_
57
59
58
60
59
61
60 IPython requires Python version 3 or above
62 IPython requires Python version 3 or above
61 ==========================================
63 ==========================================
62
64
63 Starting with version 6.0, IPython does not support Python 2.7, 3.0, 3.1, or
65 Starting with version 6.0, IPython does not support Python 2.7, 3.0, 3.1, or
64 3.2.
66 3.2.
65
67
66 For a version compatible with Python 2.7, please install the 5.x LTS Long Term
68 For a version compatible with Python 2.7, please install the 5.x LTS Long Term
67 Support version.
69 Support version.
68
70
69 If you are encountering this error message you are likely trying to install or
71 If you are encountering this error message you are likely trying to install or
70 use IPython from source. You need to checkout the remote 5.x branch. If you are
72 use IPython from source. You need to checkout the remote 5.x branch. If you are
71 using git the following should work:
73 using git the following should work:
72
74
73 $ git fetch origin
75 $ git fetch origin
74 $ git checkout 5.x
76 $ git checkout 5.x
75
77
76 If you encounter this error message with a regular install of IPython, then you
78 If you encounter this error message with a regular install of IPython, then you
77 likely need to update your package manager, for example if you are using `pip`
79 likely need to update your package manager, for example if you are using `pip`
78 check the version of pip with
80 check the version of pip with
79
81
80 $ pip --version
82 $ pip --version
81
83
82 You will need to update pip to the version 9.0.1 or greater. If you are not using
84 You will need to update pip to the version 9.0.1 or greater. If you are not using
83 pip, please inquiry with the maintainers of the package for your package
85 pip, please inquiry with the maintainers of the package for your package
84 manager.
86 manager.
85
87
86 For more information see one of our blog posts:
88 For more information see one of our blog posts:
87
89
88 http://blog.jupyter.org/2016/07/08/ipython-5-0-released/
90 http://blog.jupyter.org/2016/07/08/ipython-5-0-released/
89
91
90 As well as the following Pull-Request for discussion:
92 As well as the following Pull-Request for discussion:
91
93
92 https://github.com/ipython/ipython/pull/9900
94 https://github.com/ipython/ipython/pull/9900
93
95
94 This error does also occur if you are invoking ``setup.py`` directly – which you
96 This error does also occur if you are invoking ``setup.py`` directly – which you
95 should not – or are using ``easy_install`` If this is the case, use ``pip
97 should not – or are using ``easy_install`` If this is the case, use ``pip
96 install .`` (instead of ``setup.py install`` , and ``pip install -e .`` instead
98 install .`` (instead of ``setup.py install`` , and ``pip install -e .`` instead
97 of ``setup.py develop`` If you are depending on IPython as a dependency you may
99 of ``setup.py develop`` If you are depending on IPython as a dependency you may
98 also want to have a conditional dependency on IPython depending on the Python
100 also want to have a conditional dependency on IPython depending on the Python
99 version::
101 version::
100
102
101 install_req = ['ipython']
103 install_req = ['ipython']
102 if sys.version_info[0] < 3 and 'bdist_wheel' not in sys.argv:
104 if sys.version_info[0] < 3 and 'bdist_wheel' not in sys.argv:
103 install_req.remove('ipython')
105 install_req.remove('ipython')
104 install_req.append('ipython<6')
106 install_req.append('ipython<6')
105
107
106 setup(
108 setup(
107 ...
109 ...
108 install_requires=install_req
110 install_requires=install_req
109 )
111 )
@@ -1,256 +1,257 b''
1 .. _overview:
1 .. _overview:
2
2
3 ========
3 ========
4 Overview
4 Overview
5 ========
5 ========
6
6
7 One of Python's most useful features is its interactive interpreter.
7 One of Python's most useful features is its interactive interpreter.
8 It allows for very fast testing of ideas without the overhead of
8 It allows for very fast testing of ideas without the overhead of
9 creating test files as is typical in most programming languages.
9 creating test files as is typical in most programming languages.
10 However, the interpreter supplied with the standard Python distribution
10 However, the interpreter supplied with the standard Python distribution
11 is somewhat limited for extended interactive use.
11 is somewhat limited for extended interactive use.
12
12
13 The goal of IPython is to create a comprehensive environment for
13 The goal of IPython is to create a comprehensive environment for
14 interactive and exploratory computing. To support this goal, IPython
14 interactive and exploratory computing. To support this goal, IPython
15 has three main components:
15 has three main components:
16
16
17 * An enhanced interactive Python shell.
17 * An enhanced interactive Python shell.
18
18
19 * A decoupled :ref:`two-process communication model <ipythonzmq>`, which
19 * A decoupled :ref:`two-process communication model <ipythonzmq>`, which
20 allows for multiple clients to connect to a computation kernel, most notably
20 allows for multiple clients to connect to a computation kernel, most notably
21 the web-based notebook provided with `Jupyter <https://jupyter.org>`_.
21 the web-based notebook provided with `Jupyter <https://jupyter.org>`_.
22
22
23 * An architecture for interactive parallel computing now part of the
23 * An architecture for interactive parallel computing now part of the
24 `ipyparallel` package.
24 `ipyparallel` package.
25
25
26 All of IPython is open source (released under the revised BSD license).
26 All of IPython is open source (released under the revised BSD license).
27
27
28 Enhanced interactive Python shell
28 Enhanced interactive Python shell
29 =================================
29 =================================
30
30
31 IPython's interactive shell (:command:`ipython`), has the following goals,
31 IPython's interactive shell (:command:`ipython`), has the following goals,
32 amongst others:
32 amongst others:
33
33
34 1. Provide an interactive shell superior to Python's default. IPython
34 1. Provide an interactive shell superior to Python's default. IPython
35 has many features for tab-completion, object introspection, system shell
35 has many features for tab-completion, object introspection, system shell
36 access, command history retrieval across sessions, and its own special
36 access, command history retrieval across sessions, and its own special
37 command system for adding functionality when working interactively. It
37 command system for adding functionality when working interactively. It
38 tries to be a very efficient environment both for Python code development
38 tries to be a very efficient environment both for Python code development
39 and for exploration of problems using Python objects (in situations like
39 and for exploration of problems using Python objects (in situations like
40 data analysis).
40 data analysis).
41
41
42 2. Serve as an embeddable, ready to use interpreter for your own
42 2. Serve as an embeddable, ready to use interpreter for your own
43 programs. An interactive IPython shell can be started with a single call
43 programs. An interactive IPython shell can be started with a single call
44 from inside another program, providing access to the current namespace.
44 from inside another program, providing access to the current namespace.
45 This can be very useful both for debugging purposes and for situations
45 This can be very useful both for debugging purposes and for situations
46 where a blend of batch-processing and interactive exploration are needed.
46 where a blend of batch-processing and interactive exploration are needed.
47
47
48 3. Offer a flexible framework which can be used as the base
48 3. Offer a flexible framework which can be used as the base
49 environment for working with other systems, with Python as the underlying
49 environment for working with other systems, with Python as the underlying
50 bridge language. Specifically scientific environments like Mathematica,
50 bridge language. Specifically scientific environments like Mathematica,
51 IDL and Matlab inspired its design, but similar ideas can be
51 IDL and Matlab inspired its design, but similar ideas can be
52 useful in many fields.
52 useful in many fields.
53
53
54 4. Allow interactive testing of threaded graphical toolkits. IPython
54 4. Allow interactive testing of threaded graphical toolkits. IPython
55 has support for interactive, non-blocking control of GTK, Qt, WX, GLUT, and
55 has support for interactive, non-blocking control of GTK, Qt, WX, GLUT, and
56 OS X applications via special threading flags. The normal Python
56 OS X applications via special threading flags. The normal Python
57 shell can only do this for Tkinter applications.
57 shell can only do this for Tkinter applications.
58
58
59 Main features of the interactive shell
59 Main features of the interactive shell
60 --------------------------------------
60 --------------------------------------
61
61
62 * Dynamic object introspection. One can access docstrings, function
62 * Dynamic object introspection. One can access docstrings, function
63 definition prototypes, source code, source files and other details
63 definition prototypes, source code, source files and other details
64 of any object accessible to the interpreter with a single
64 of any object accessible to the interpreter with a single
65 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
65 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
66
66
67 * Searching through modules and namespaces with :samp:`*` wildcards, both
67 * Searching through modules and namespaces with :samp:`*` wildcards, both
68 when using the :samp:`?` system and via the :samp:`%psearch` command.
68 when using the :samp:`?` system and via the :samp:`%psearch` command.
69
69
70 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
70 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
71 This works for keywords, modules, methods, variables and files in the
71 This works for keywords, modules, methods, variables and files in the
72 current directory. This is supported via the ``prompt_toolkit`` library.
72 current directory. This is supported via the ``prompt_toolkit`` library.
73 Custom completers can be implemented easily for different purposes
73 Custom completers can be implemented easily for different purposes
74 (system commands, magic arguments etc.)
74 (system commands, magic arguments etc.)
75
75
76 * Numbered input/output prompts with command history (persistent
76 * Numbered input/output prompts with command history (persistent
77 across sessions and tied to each profile), full searching in this
77 across sessions and tied to each profile), full searching in this
78 history and caching of all input and output.
78 history and caching of all input and output.
79
79
80 * User-extensible 'magic' commands. A set of commands prefixed with
80 * User-extensible 'magic' commands. A set of commands prefixed with
81 :samp:`%` or :samp:`%%` is available for controlling IPython itself and provides
81 :samp:`%` or :samp:`%%` is available for controlling IPython itself and provides
82 directory control, namespace information and many aliases to
82 directory control, namespace information and many aliases to
83 common system shell commands.
83 common system shell commands.
84
84
85 * Alias facility for defining your own system aliases.
85 * Alias facility for defining your own system aliases.
86
86
87 * Complete system shell access. Lines starting with :samp:`!` are passed
87 * Complete system shell access. Lines starting with :samp:`!` are passed
88 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
88 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
89 captures shell output into python variables for further use.
89 captures shell output into python variables for further use.
90
90
91 * The ability to expand python variables when calling the system shell. In a
91 * The ability to expand python variables when calling the system shell. In a
92 shell command, any python variable prefixed with :samp:`$` is expanded. A
92 shell command, any python variable prefixed with :samp:`$` is expanded. A
93 double :samp:`$$` allows passing a literal :samp:`$` to the shell (for access
93 double :samp:`$$` allows passing a literal :samp:`$` to the shell (for access
94 to shell and environment variables like :envvar:`PATH`).
94 to shell and environment variables like :envvar:`PATH`).
95
95
96 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
96 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
97 persistent bookmark system (using :samp:`%bookmark`) for fast access to
97 persistent bookmark system (using :samp:`%bookmark`) for fast access to
98 frequently visited directories.
98 frequently visited directories.
99
99
100 * A lightweight persistence framework via the :samp:`%store` command, which
100 * A lightweight persistence framework via the :samp:`%store` command, which
101 allows you to save arbitrary Python variables. These get restored
101 allows you to save arbitrary Python variables. These get restored
102 when you run the :samp:`%store -r` command.
102 when you run the :samp:`%store -r` command.
103
103
104 * Automatic indentation and highlighting of code as you type (through the
104 * Automatic indentation and highlighting of code as you type (through the
105 `prompt_toolkit` library).
105 `prompt_toolkit` library).
106
106
107 * Macro system for quickly re-executing multiple lines of previous
107 * Macro system for quickly re-executing multiple lines of previous
108 input with a single name via the :samp:`%macro` command. Macros can be
108 input with a single name via the :samp:`%macro` command. Macros can be
109 stored persistently via :samp:`%store` and edited via :samp:`%edit`.
109 stored persistently via :samp:`%store` and edited via :samp:`%edit`.
110
110
111 * Session logging (you can then later use these logs as code in your
111 * Session logging (you can then later use these logs as code in your
112 programs). Logs can optionally timestamp all input, and also store
112 programs). Logs can optionally timestamp all input, and also store
113 session output (marked as comments, so the log remains valid
113 session output (marked as comments, so the log remains valid
114 Python source code).
114 Python source code).
115
115
116 * Session restoring: logs can be replayed to restore a previous
116 * Session restoring: logs can be replayed to restore a previous
117 session to the state where you left it.
117 session to the state where you left it.
118
118
119 * Verbose and colored exception traceback printouts. Easier to parse
119 * Verbose and colored exception traceback printouts. Easier to parse
120 visually, and in verbose mode they produce a lot of useful
120 visually, and in verbose mode they produce a lot of useful
121 debugging information (basically a terminal version of the cgitb
121 debugging information (basically a terminal version of the cgitb
122 module).
122 module).
123
123
124 * Auto-parentheses via the :samp:`%autocall` command: callable objects can be
124 * Auto-parentheses via the :samp:`%autocall` command: callable objects can be
125 executed without parentheses: :samp:`sin 3` is automatically converted to
125 executed without parentheses: :samp:`sin 3` is automatically converted to
126 :samp:`sin(3)`
126 :samp:`sin(3)`
127
127
128 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
128 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
129 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
129 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
130 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
130 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
131 becomes :samp:`my_function("a b")`.
131 becomes :samp:`my_function("a b")`.
132
132
133 * Extensible input syntax. You can define filters that pre-process
133 * Extensible input syntax. You can define filters that pre-process
134 user input to simplify input in special situations. This allows
134 user input to simplify input in special situations. This allows
135 for example pasting multi-line code fragments which start with
135 for example pasting multi-line code fragments which start with
136 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
136 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
137 standard Python documentation.
137 standard Python documentation.
138
138
139 * Flexible :ref:`configuration system <config_overview>`. It uses a
139 * Flexible :ref:`configuration system <config_overview>`. It uses a
140 configuration file which allows permanent setting of all command-line
140 configuration file which allows permanent setting of all command-line
141 options, module loading, code and file execution. The system allows
141 options, module loading, code and file execution. The system allows
142 recursive file inclusion, so you can have a base file with defaults and
142 recursive file inclusion, so you can have a base file with defaults and
143 layers which load other customizations for particular projects.
143 layers which load other customizations for particular projects.
144
144
145 * Embeddable. You can call IPython as a python shell inside your own
145 * Embeddable. You can call IPython as a python shell inside your own
146 python programs. This can be used both for debugging code or for
146 python programs. This can be used both for debugging code or for
147 providing interactive abilities to your programs with knowledge
147 providing interactive abilities to your programs with knowledge
148 about the local namespaces (very useful in debugging and data
148 about the local namespaces (very useful in debugging and data
149 analysis situations).
149 analysis situations).
150
150
151 * Easy debugger access. You can set IPython to call up an enhanced version of
151 * Easy debugger access. You can set IPython to call up an enhanced version of
152 the Python debugger (pdb) every time there is an uncaught exception. This
152 the Python debugger (pdb) every time there is an uncaught exception. This
153 drops you inside the code which triggered the exception with all the data
153 drops you inside the code which triggered the exception with all the data
154 live and it is possible to navigate the stack to rapidly isolate the source
154 live and it is possible to navigate the stack to rapidly isolate the source
155 of a bug. The :samp:`%run` magic command (with the :samp:`-d` option) can run
155 of a bug. The :samp:`%run` magic command (with the :samp:`-d` option) can run
156 any script under pdb's control, automatically setting initial breakpoints for
156 any script under pdb's control, automatically setting initial breakpoints for
157 you. This version of pdb has IPython-specific improvements, including
157 you. This version of pdb has IPython-specific improvements, including
158 tab-completion and traceback coloring support. For even easier debugger
158 tab-completion and traceback coloring support. For even easier debugger
159 access, try :samp:`%debug` after seeing an exception.
159 access, try :samp:`%debug` after seeing an exception.
160
160
161 * Profiler support. You can run single statements (similar to
161 * Profiler support. You can run single statements (similar to
162 :samp:`profile.run()`) or complete programs under the profiler's control.
162 :samp:`profile.run()`) or complete programs under the profiler's control.
163 While this is possible with standard cProfile or profile modules,
163 While this is possible with standard cProfile or profile modules,
164 IPython wraps this functionality with magic commands (see :samp:`%prun`
164 IPython wraps this functionality with magic commands (see :samp:`%prun`
165 and :samp:`%run -p`) convenient for rapid interactive work.
165 and :samp:`%run -p`) convenient for rapid interactive work.
166
166
167 * Simple timing information. You can use the :samp:`%timeit` command to get
167 * Simple timing information. You can use the :samp:`%timeit` command to get
168 the execution time of a Python statement or expression. This machinery is
168 the execution time of a Python statement or expression. This machinery is
169 intelligent enough to do more repetitions for commands that finish very
169 intelligent enough to do more repetitions for commands that finish very
170 quickly in order to get a better estimate of their running time.
170 quickly in order to get a better estimate of their running time.
171
171
172 .. sourcecode:: ipython
172 .. sourcecode:: ipython
173
173
174 In [1]: %timeit 1+1
174 In [1]: %timeit 1+1
175 10000000 loops, best of 3: 25.5 ns per loop
175 10000000 loops, best of 3: 25.5 ns per loop
176
176
177 In [2]: %timeit [math.sin(x) for x in range(5000)]
177 In [2]: %timeit [math.sin(x) for x in range(5000)]
178 1000 loops, best of 3: 719 Β΅s per loop
178 1000 loops, best of 3: 719 Β΅s per loop
179
179
180 ..
180 ..
181
181
182 To get the timing information for more than one expression, use the
182 To get the timing information for more than one expression, use the
183 :samp:`%%timeit` cell magic command.
183 :samp:`%%timeit` cell magic command.
184
184
185
185
186 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
186 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
187 to use doctest-compatible prompts, so you can use IPython sessions as
187 to use doctest-compatible prompts, so you can use IPython sessions as
188 doctest code. By default, IPython also allows you to paste existing
188 doctest code. By default, IPython also allows you to paste existing
189 doctests, and strips out the leading :samp:`>>>` and :samp:`...` prompts in
189 doctests, and strips out the leading :samp:`>>>` and :samp:`...` prompts in
190 them.
190 them.
191
191
192 .. _ipythonzmq:
192 .. _ipythonzmq:
193
193
194 Decoupled two-process model
194 Decoupled two-process model
195 ==============================
195 ==============================
196
196
197 IPython has abstracted and extended the notion of a traditional
197 IPython has abstracted and extended the notion of a traditional
198 *Read-Evaluate-Print Loop* (REPL) environment by decoupling the *evaluation*
198 *Read-Evaluate-Print Loop* (REPL) environment by decoupling the *evaluation*
199 into its own process. We call this process a **kernel**: it receives execution
199 into its own process. We call this process a **kernel**: it receives execution
200 instructions from clients and communicates the results back to them.
200 instructions from clients and communicates the results back to them.
201
201
202 This decoupling allows us to have several clients connected to the same
202 This decoupling allows us to have several clients connected to the same
203 kernel, and even allows clients and kernels to live on different machines.
203 kernel, and even allows clients and kernels to live on different machines.
204 With the exclusion of the traditional single process terminal-based IPython
204 With the exclusion of the traditional single process terminal-based IPython
205 (what you start if you run ``ipython`` without any subcommands), all
205 (what you start if you run ``ipython`` without any subcommands), all
206 other IPython machinery uses this two-process model. Most of this is now part
206 other IPython machinery uses this two-process model. Most of this is now part
207 of the `Jupyter` project, which includes ``jupyter console``, ``jupyter
207 of the `Jupyter` project, which includes ``jupyter console``, ``jupyter
208 qtconsole``, and ``jupyter notebook``.
208 qtconsole``, and ``jupyter notebook``.
209
209
210 As an example, this means that when you start ``jupyter qtconsole``, you're
210 As an example, this means that when you start ``jupyter qtconsole``, you're
211 really starting two processes, a kernel and a Qt-based client can send
211 really starting two processes, a kernel and a Qt-based client can send
212 commands to and receive results from that kernel. If there is already a kernel
212 commands to and receive results from that kernel. If there is already a kernel
213 running that you want to connect to, you can pass the ``--existing`` flag
213 running that you want to connect to, you can pass the ``--existing`` flag
214 which will skip initiating a new kernel and connect to the most recent kernel,
214 which will skip initiating a new kernel and connect to the most recent kernel,
215 instead. To connect to a specific kernel once you have several kernels
215 instead. To connect to a specific kernel once you have several kernels
216 running, use the ``%connect_info`` magic to get the unique connection file,
216 running, use the ``%connect_info`` magic to get the unique connection file,
217 which will be something like ``--existing kernel-19732.json`` but with
217 which will be something like ``--existing kernel-19732.json`` but with
218 different numbers which correspond to the Process ID of the kernel.
218 different numbers which correspond to the Process ID of the kernel.
219
219
220 You can read more about using `jupyter qtconsole
220 You can read more about using `jupyter qtconsole
221 <http://jupyter.org/qtconsole/>`_, and
221 <http://jupyter.org/qtconsole/>`_, and
222 `jupyter notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`_. There
222 `jupyter notebook <http://jupyter-notebook.readthedocs.io/en/latest/>`_. There
223 is also a :ref:`message spec <messaging>` which documents the protocol for
223 is also a :ref:`message spec <messaging>` which documents the protocol for
224 communication between kernels
224 communication between kernels
225 and clients.
225 and clients.
226
226
227 .. seealso::
227 .. seealso::
228
228
229 `Frontend/Kernel Model`_ example notebook
229 `Frontend/Kernel Model`_ example notebook
230
230
231
231
232 Interactive parallel computing
232 Interactive parallel computing
233 ==============================
233 ==============================
234
234
235
235
236 This functionality is optional and now part of the `ipyparallel
236 This functionality is optional and now part of the `ipyparallel
237 <http://ipyparallel.readthedocs.io/>`_ project.
237 <http://ipyparallel.readthedocs.io/>`_ project.
238
238
239 Portability and Python requirements
239 Portability and Python requirements
240 -----------------------------------
240 -----------------------------------
241
241
242 Version 6.0+ supports compatibility with Python 3.3 and higher.
242 Version 6.3+ supports Python 3.4 and higher.
243 Versions 6.0 to 6.2 support Python 3.3 and higher.
243 Versions 2.0 to 5.x work with Python 2.7.x releases and Python 3.3 and higher.
244 Versions 2.0 to 5.x work with Python 2.7.x releases and Python 3.3 and higher.
244 Version 1.0 additionally worked with Python 2.6 and 3.2.
245 Version 1.0 additionally worked with Python 2.6 and 3.2.
245 Version 0.12 was the first version to fully support Python 3.
246 Version 0.12 was the first version to fully support Python 3.
246
247
247 IPython is known to work on the following operating systems:
248 IPython is known to work on the following operating systems:
248
249
249 * Linux
250 * Linux
250 * Most other Unix-like OSs (AIX, Solaris, BSD, etc.)
251 * Most other Unix-like OSs (AIX, Solaris, BSD, etc.)
251 * Mac OS X
252 * Mac OS X
252 * Windows (CygWin, XP, Vista, etc.)
253 * Windows (CygWin, XP, Vista, etc.)
253
254
254 See :ref:`here <install_index>` for instructions on how to install IPython.
255 See :ref:`here <install_index>` for instructions on how to install IPython.
255
256
256 .. include:: links.txt
257 .. include:: links.txt
@@ -1,264 +1,264 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 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import sys
23 import sys
24
24
25 # **Python version check**
25 # **Python version check**
26 #
26 #
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 if sys.version_info < (3, 4):
29 if sys.version_info < (3, 4):
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
30 pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.'
31 try:
31 try:
32 import pip
32 import pip
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
33 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
34 if pip_version < (9, 0, 1) :
34 if pip_version < (9, 0, 1) :
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
35 pip_message = 'Your pip version is out of date, please install pip >= 9.0.1. '\
36 'pip {} detected.'.format(pip.__version__)
36 'pip {} detected.'.format(pip.__version__)
37 else:
37 else:
38 # pip is new enough - it must be something else
38 # pip is new enough - it must be something else
39 pip_message = ''
39 pip_message = ''
40 except Exception:
40 except Exception:
41 pass
41 pass
42
42
43
43
44 error = """
44 error = """
45 IPython 6.3+ supports Python 3.4 and above.
45 IPython 6.3+ supports Python 3.4 and above.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
46 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
47 Python 3.3 was supported up to IPython 6.2.
47 Python 3.3 was supported up to IPython 6.2.
48
48
49 See IPython `README.rst` file for more information:
49 See IPython `README.rst` file for more information:
50
50
51 https://github.com/ipython/ipython/blob/master/README.rst
51 https://github.com/ipython/ipython/blob/master/README.rst
52
52
53 Python {py} detected.
53 Python {py} detected.
54 {pip}
54 {pip}
55 """.format(py=sys.version_info, pip=pip_message )
55 """.format(py=sys.version_info, pip=pip_message )
56
56
57 print(error, file=sys.stderr)
57 print(error, file=sys.stderr)
58 sys.exit(1)
58 sys.exit(1)
59
59
60 # At least we're on the python version we need, move on.
60 # At least we're on the python version we need, move on.
61
61
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
62 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
63 # update it when the contents of directories change.
63 # update it when the contents of directories change.
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
64 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
65
65
66 from distutils.core import setup
66 from distutils.core import setup
67
67
68 # Our own imports
68 # Our own imports
69 from setupbase import target_update
69 from setupbase import target_update
70
70
71 from setupbase import (
71 from setupbase import (
72 setup_args,
72 setup_args,
73 find_packages,
73 find_packages,
74 find_package_data,
74 find_package_data,
75 check_package_data_first,
75 check_package_data_first,
76 find_entry_points,
76 find_entry_points,
77 build_scripts_entrypt,
77 build_scripts_entrypt,
78 find_data_files,
78 find_data_files,
79 git_prebuild,
79 git_prebuild,
80 install_symlinked,
80 install_symlinked,
81 install_lib_symlink,
81 install_lib_symlink,
82 install_scripts_for_symlink,
82 install_scripts_for_symlink,
83 unsymlink,
83 unsymlink,
84 )
84 )
85
85
86 isfile = os.path.isfile
86 isfile = os.path.isfile
87 pjoin = os.path.join
87 pjoin = os.path.join
88
88
89 #-------------------------------------------------------------------------------
89 #-------------------------------------------------------------------------------
90 # Handle OS specific things
90 # Handle OS specific things
91 #-------------------------------------------------------------------------------
91 #-------------------------------------------------------------------------------
92
92
93 if os.name in ('nt','dos'):
93 if os.name in ('nt','dos'):
94 os_name = 'windows'
94 os_name = 'windows'
95 else:
95 else:
96 os_name = os.name
96 os_name = os.name
97
97
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
98 # Under Windows, 'sdist' has not been supported. Now that the docs build with
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
99 # Sphinx it might work, but let's not turn it on until someone confirms that it
100 # actually works.
100 # actually works.
101 if os_name == 'windows' and 'sdist' in sys.argv:
101 if os_name == 'windows' and 'sdist' in sys.argv:
102 print('The sdist command is not available under Windows. Exiting.')
102 print('The sdist command is not available under Windows. Exiting.')
103 sys.exit(1)
103 sys.exit(1)
104
104
105
105
106 #-------------------------------------------------------------------------------
106 #-------------------------------------------------------------------------------
107 # Things related to the IPython documentation
107 # Things related to the IPython documentation
108 #-------------------------------------------------------------------------------
108 #-------------------------------------------------------------------------------
109
109
110 # update the manuals when building a source dist
110 # update the manuals when building a source dist
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
111 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
112
112
113 # List of things to be updated. Each entry is a triplet of args for
113 # List of things to be updated. Each entry is a triplet of args for
114 # target_update()
114 # target_update()
115 to_update = [
115 to_update = [
116 ('docs/man/ipython.1.gz',
116 ('docs/man/ipython.1.gz',
117 ['docs/man/ipython.1'],
117 ['docs/man/ipython.1'],
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
118 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
119 ]
119 ]
120
120
121
121
122 [ target_update(*t) for t in to_update ]
122 [ target_update(*t) for t in to_update ]
123
123
124 #---------------------------------------------------------------------------
124 #---------------------------------------------------------------------------
125 # Find all the packages, package data, and data_files
125 # Find all the packages, package data, and data_files
126 #---------------------------------------------------------------------------
126 #---------------------------------------------------------------------------
127
127
128 packages = find_packages()
128 packages = find_packages()
129 package_data = find_package_data()
129 package_data = find_package_data()
130
130
131 data_files = find_data_files()
131 data_files = find_data_files()
132
132
133 setup_args['packages'] = packages
133 setup_args['packages'] = packages
134 setup_args['package_data'] = package_data
134 setup_args['package_data'] = package_data
135 setup_args['data_files'] = data_files
135 setup_args['data_files'] = data_files
136
136
137 #---------------------------------------------------------------------------
137 #---------------------------------------------------------------------------
138 # custom distutils commands
138 # custom distutils commands
139 #---------------------------------------------------------------------------
139 #---------------------------------------------------------------------------
140 # imports here, so they are after setuptools import if there was one
140 # imports here, so they are after setuptools import if there was one
141 from distutils.command.sdist import sdist
141 from distutils.command.sdist import sdist
142
142
143 setup_args['cmdclass'] = {
143 setup_args['cmdclass'] = {
144 'build_py': \
144 'build_py': \
145 check_package_data_first(git_prebuild('IPython')),
145 check_package_data_first(git_prebuild('IPython')),
146 'sdist' : git_prebuild('IPython', sdist),
146 'sdist' : git_prebuild('IPython', sdist),
147 'symlink': install_symlinked,
147 'symlink': install_symlinked,
148 'install_lib_symlink': install_lib_symlink,
148 'install_lib_symlink': install_lib_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
149 'install_scripts_sym': install_scripts_for_symlink,
150 'unsymlink': unsymlink,
150 'unsymlink': unsymlink,
151 }
151 }
152
152
153
153
154 #---------------------------------------------------------------------------
154 #---------------------------------------------------------------------------
155 # Handle scripts, dependencies, and setuptools specific things
155 # Handle scripts, dependencies, and setuptools specific things
156 #---------------------------------------------------------------------------
156 #---------------------------------------------------------------------------
157
157
158 # For some commands, use setuptools. Note that we do NOT list install here!
158 # For some commands, use setuptools. Note that we do NOT list install here!
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
159 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
160 needs_setuptools = {'develop', 'release', 'bdist_egg', 'bdist_rpm',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
161 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
162 'egg_info', 'easy_install', 'upload', 'install_egg_info',
163 }
163 }
164
164
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
165 if len(needs_setuptools.intersection(sys.argv)) > 0:
166 import setuptools
166 import setuptools
167
167
168 # This dict is used for passing extra arguments that are setuptools
168 # This dict is used for passing extra arguments that are setuptools
169 # specific to setup
169 # specific to setup
170 setuptools_extra_args = {}
170 setuptools_extra_args = {}
171
171
172 # setuptools requirements
172 # setuptools requirements
173
173
174 extras_require = dict(
174 extras_require = dict(
175 parallel = ['ipyparallel'],
175 parallel = ['ipyparallel'],
176 qtconsole = ['qtconsole'],
176 qtconsole = ['qtconsole'],
177 doc = ['Sphinx>=1.3'],
177 doc = ['Sphinx>=1.3'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
178 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
179 terminal = [],
179 terminal = [],
180 kernel = ['ipykernel'],
180 kernel = ['ipykernel'],
181 nbformat = ['nbformat'],
181 nbformat = ['nbformat'],
182 notebook = ['notebook', 'ipywidgets'],
182 notebook = ['notebook', 'ipywidgets'],
183 nbconvert = ['nbconvert'],
183 nbconvert = ['nbconvert'],
184 )
184 )
185
185
186 install_requires = [
186 install_requires = [
187 'setuptools>=18.5',
187 'setuptools>=18.5',
188 'jedi>=0.10',
188 'jedi>=0.10',
189 'decorator',
189 'decorator',
190 'pickleshare',
190 'pickleshare',
191 'simplegeneric>0.8',
191 'simplegeneric>0.8',
192 'traitlets>=4.2',
192 'traitlets>=4.2',
193 'prompt_toolkit>=1.0.15,<2.0.0',
193 'prompt_toolkit>=1.0.15,<2.0.0',
194 'pygments',
194 'pygments',
195 'backcall',
195 'backcall',
196 ]
196 ]
197
197
198 # Platform-specific dependencies:
198 # Platform-specific dependencies:
199 # This is the correct way to specify these,
199 # This is the correct way to specify these,
200 # but requires pip >= 6. pip < 6 ignores these.
200 # but requires pip >= 6. pip < 6 ignores these.
201
201
202 extras_require.update({
202 extras_require.update({
203 ':python_version <= "3.4"': ['typing'],
203 ':python_version == "3.4"': ['typing'],
204 ':sys_platform != "win32"': ['pexpect'],
204 ':sys_platform != "win32"': ['pexpect'],
205 ':sys_platform == "darwin"': ['appnope'],
205 ':sys_platform == "darwin"': ['appnope'],
206 ':sys_platform == "win32"': ['colorama'],
206 ':sys_platform == "win32"': ['colorama'],
207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
207 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
208 })
208 })
209 # FIXME: re-specify above platform dependencies for pip < 6
209 # FIXME: re-specify above platform dependencies for pip < 6
210 # These would result in non-portable bdists.
210 # These would result in non-portable bdists.
211 if not any(arg.startswith('bdist') for arg in sys.argv):
211 if not any(arg.startswith('bdist') for arg in sys.argv):
212 if sys.platform == 'darwin':
212 if sys.platform == 'darwin':
213 install_requires.extend(['appnope'])
213 install_requires.extend(['appnope'])
214
214
215 if not sys.platform.startswith('win'):
215 if not sys.platform.startswith('win'):
216 install_requires.append('pexpect')
216 install_requires.append('pexpect')
217
217
218 # workaround pypa/setuptools#147, where setuptools misspells
218 # workaround pypa/setuptools#147, where setuptools misspells
219 # platform_python_implementation as python_implementation
219 # platform_python_implementation as python_implementation
220 if 'setuptools' in sys.modules:
220 if 'setuptools' in sys.modules:
221 for key in list(extras_require):
221 for key in list(extras_require):
222 if 'platform_python_implementation' in key:
222 if 'platform_python_implementation' in key:
223 new_key = key.replace('platform_python_implementation', 'python_implementation')
223 new_key = key.replace('platform_python_implementation', 'python_implementation')
224 extras_require[new_key] = extras_require.pop(key)
224 extras_require[new_key] = extras_require.pop(key)
225
225
226 everything = set()
226 everything = set()
227 for key, deps in extras_require.items():
227 for key, deps in extras_require.items():
228 if ':' not in key:
228 if ':' not in key:
229 everything.update(deps)
229 everything.update(deps)
230 extras_require['all'] = everything
230 extras_require['all'] = everything
231
231
232 if 'setuptools' in sys.modules:
232 if 'setuptools' in sys.modules:
233 setuptools_extra_args['python_requires'] = '>=3.4'
233 setuptools_extra_args['python_requires'] = '>=3.4'
234 setuptools_extra_args['zip_safe'] = False
234 setuptools_extra_args['zip_safe'] = False
235 setuptools_extra_args['entry_points'] = {
235 setuptools_extra_args['entry_points'] = {
236 'console_scripts': find_entry_points(),
236 'console_scripts': find_entry_points(),
237 'pygments.lexers': [
237 'pygments.lexers': [
238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
238 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
239 'ipython = IPython.lib.lexers:IPythonLexer',
239 'ipython = IPython.lib.lexers:IPythonLexer',
240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
240 'ipython3 = IPython.lib.lexers:IPython3Lexer',
241 ],
241 ],
242 }
242 }
243 setup_args['extras_require'] = extras_require
243 setup_args['extras_require'] = extras_require
244 setup_args['install_requires'] = install_requires
244 setup_args['install_requires'] = install_requires
245
245
246 else:
246 else:
247 # scripts has to be a non-empty list, or install_scripts isn't called
247 # scripts has to be a non-empty list, or install_scripts isn't called
248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
248 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
249
249
250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
250 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
251
251
252 #---------------------------------------------------------------------------
252 #---------------------------------------------------------------------------
253 # Do the actual setup now
253 # Do the actual setup now
254 #---------------------------------------------------------------------------
254 #---------------------------------------------------------------------------
255
255
256 setup_args.update(setuptools_extra_args)
256 setup_args.update(setuptools_extra_args)
257
257
258
258
259
259
260 def main():
260 def main():
261 setup(**setup_args)
261 setup(**setup_args)
262
262
263 if __name__ == '__main__':
263 if __name__ == '__main__':
264 main()
264 main()
General Comments 0
You need to be logged in to leave comments. Login now