##// END OF EJS Templates
Fix documentation typos...
luz.paz -
Show More
@@ -1,155 +1,155 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 Support for creating GUI apps and starting event loops.
3 Support for creating GUI apps and starting event loops.
4
4
5 IPython's GUI integration allows interative plotting and GUI usage in IPython
5 IPython's GUI integration allows interactive plotting and GUI usage in IPython
6 session. IPython has two different types of GUI integration:
6 session. IPython has two different types of GUI integration:
7
7
8 1. The terminal based IPython supports GUI event loops through Python's
8 1. The terminal based IPython supports GUI event loops through Python's
9 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
9 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
10 whenever raw_input is waiting for a user to type code. We implement GUI
10 whenever raw_input is waiting for a user to type code. We implement GUI
11 support in the terminal by setting PyOS_InputHook to a function that
11 support in the terminal by setting PyOS_InputHook to a function that
12 iterates the event loop for a short while. It is important to note that
12 iterates the event loop for a short while. It is important to note that
13 in this situation, the real GUI event loop is NOT run in the normal
13 in this situation, the real GUI event loop is NOT run in the normal
14 manner, so you can't use the normal means to detect that it is running.
14 manner, so you can't use the normal means to detect that it is running.
15 2. In the two process IPython kernel/frontend, the GUI event loop is run in
15 2. In the two process IPython kernel/frontend, the GUI event loop is run in
16 the kernel. In this case, the event loop is run in the normal manner by
16 the kernel. In this case, the event loop is run in the normal manner by
17 calling the function or method of the GUI toolkit that starts the event
17 calling the function or method of the GUI toolkit that starts the event
18 loop.
18 loop.
19
19
20 In addition to starting the GUI event loops in one of these two ways, IPython
20 In addition to starting the GUI event loops in one of these two ways, IPython
21 will *always* create an appropriate GUI application object when GUi
21 will *always* create an appropriate GUI application object when GUi
22 integration is enabled.
22 integration is enabled.
23
23
24 If you want your GUI apps to run in IPython you need to do two things:
24 If you want your GUI apps to run in IPython you need to do two things:
25
25
26 1. Test to see if there is already an existing main application object. If
26 1. Test to see if there is already an existing main application object. If
27 there is, you should use it. If there is not an existing application object
27 there is, you should use it. If there is not an existing application object
28 you should create one.
28 you should create one.
29 2. Test to see if the GUI event loop is running. If it is, you should not
29 2. Test to see if the GUI event loop is running. If it is, you should not
30 start it. If the event loop is not running you may start it.
30 start it. If the event loop is not running you may start it.
31
31
32 This module contains functions for each toolkit that perform these things
32 This module contains functions for each toolkit that perform these things
33 in a consistent manner. Because of how PyOS_InputHook runs the event loop
33 in a consistent manner. Because of how PyOS_InputHook runs the event loop
34 you cannot detect if the event loop is running using the traditional calls
34 you cannot detect if the event loop is running using the traditional calls
35 (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is
35 (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is
36 set These methods will return a false negative. That is, they will say the
36 set These methods will return a false negative. That is, they will say the
37 event loop is not running, when is actually is. To work around this limitation
37 event loop is not running, when is actually is. To work around this limitation
38 we proposed the following informal protocol:
38 we proposed the following informal protocol:
39
39
40 * Whenever someone starts the event loop, they *must* set the ``_in_event_loop``
40 * Whenever someone starts the event loop, they *must* set the ``_in_event_loop``
41 attribute of the main application object to ``True``. This should be done
41 attribute of the main application object to ``True``. This should be done
42 regardless of how the event loop is actually run.
42 regardless of how the event loop is actually run.
43 * Whenever someone stops the event loop, they *must* set the ``_in_event_loop``
43 * Whenever someone stops the event loop, they *must* set the ``_in_event_loop``
44 attribute of the main application object to ``False``.
44 attribute of the main application object to ``False``.
45 * If you want to see if the event loop is running, you *must* use ``hasattr``
45 * If you want to see if the event loop is running, you *must* use ``hasattr``
46 to see if ``_in_event_loop`` attribute has been set. If it is set, you
46 to see if ``_in_event_loop`` attribute has been set. If it is set, you
47 *must* use its value. If it has not been set, you can query the toolkit
47 *must* use its value. If it has not been set, you can query the toolkit
48 in the normal manner.
48 in the normal manner.
49 * If you want GUI support and no one else has created an application or
49 * If you want GUI support and no one else has created an application or
50 started the event loop you *must* do this. We don't want projects to
50 started the event loop you *must* do this. We don't want projects to
51 attempt to defer these things to someone else if they themselves need it.
51 attempt to defer these things to someone else if they themselves need it.
52
52
53 The functions below implement this logic for each GUI toolkit. If you need
53 The functions below implement this logic for each GUI toolkit. If you need
54 to create custom application subclasses, you will likely have to modify this
54 to create custom application subclasses, you will likely have to modify this
55 code for your own purposes. This code can be copied into your own project
55 code for your own purposes. This code can be copied into your own project
56 so you don't have to depend on IPython.
56 so you don't have to depend on IPython.
57
57
58 """
58 """
59
59
60 # Copyright (c) IPython Development Team.
60 # Copyright (c) IPython Development Team.
61 # Distributed under the terms of the Modified BSD License.
61 # Distributed under the terms of the Modified BSD License.
62
62
63 from IPython.core.getipython import get_ipython
63 from IPython.core.getipython import get_ipython
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # wx
66 # wx
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 def get_app_wx(*args, **kwargs):
69 def get_app_wx(*args, **kwargs):
70 """Create a new wx app or return an exiting one."""
70 """Create a new wx app or return an exiting one."""
71 import wx
71 import wx
72 app = wx.GetApp()
72 app = wx.GetApp()
73 if app is None:
73 if app is None:
74 if 'redirect' not in kwargs:
74 if 'redirect' not in kwargs:
75 kwargs['redirect'] = False
75 kwargs['redirect'] = False
76 app = wx.PySimpleApp(*args, **kwargs)
76 app = wx.PySimpleApp(*args, **kwargs)
77 return app
77 return app
78
78
79 def is_event_loop_running_wx(app=None):
79 def is_event_loop_running_wx(app=None):
80 """Is the wx event loop running."""
80 """Is the wx event loop running."""
81 # New way: check attribute on shell instance
81 # New way: check attribute on shell instance
82 ip = get_ipython()
82 ip = get_ipython()
83 if ip is not None:
83 if ip is not None:
84 if ip.active_eventloop and ip.active_eventloop == 'wx':
84 if ip.active_eventloop and ip.active_eventloop == 'wx':
85 return True
85 return True
86 # Fall through to checking the application, because Wx has a native way
86 # Fall through to checking the application, because Wx has a native way
87 # to check if the event loop is running, unlike Qt.
87 # to check if the event loop is running, unlike Qt.
88
88
89 # Old way: check Wx application
89 # Old way: check Wx application
90 if app is None:
90 if app is None:
91 app = get_app_wx()
91 app = get_app_wx()
92 if hasattr(app, '_in_event_loop'):
92 if hasattr(app, '_in_event_loop'):
93 return app._in_event_loop
93 return app._in_event_loop
94 else:
94 else:
95 return app.IsMainLoopRunning()
95 return app.IsMainLoopRunning()
96
96
97 def start_event_loop_wx(app=None):
97 def start_event_loop_wx(app=None):
98 """Start the wx event loop in a consistent manner."""
98 """Start the wx event loop in a consistent manner."""
99 if app is None:
99 if app is None:
100 app = get_app_wx()
100 app = get_app_wx()
101 if not is_event_loop_running_wx(app):
101 if not is_event_loop_running_wx(app):
102 app._in_event_loop = True
102 app._in_event_loop = True
103 app.MainLoop()
103 app.MainLoop()
104 app._in_event_loop = False
104 app._in_event_loop = False
105 else:
105 else:
106 app._in_event_loop = True
106 app._in_event_loop = True
107
107
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109 # qt4
109 # qt4
110 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
111
111
112 def get_app_qt4(*args, **kwargs):
112 def get_app_qt4(*args, **kwargs):
113 """Create a new qt4 app or return an existing one."""
113 """Create a new qt4 app or return an existing one."""
114 from IPython.external.qt_for_kernel import QtGui
114 from IPython.external.qt_for_kernel import QtGui
115 app = QtGui.QApplication.instance()
115 app = QtGui.QApplication.instance()
116 if app is None:
116 if app is None:
117 if not args:
117 if not args:
118 args = ([''],)
118 args = ([''],)
119 app = QtGui.QApplication(*args, **kwargs)
119 app = QtGui.QApplication(*args, **kwargs)
120 return app
120 return app
121
121
122 def is_event_loop_running_qt4(app=None):
122 def is_event_loop_running_qt4(app=None):
123 """Is the qt4 event loop running."""
123 """Is the qt4 event loop running."""
124 # New way: check attribute on shell instance
124 # New way: check attribute on shell instance
125 ip = get_ipython()
125 ip = get_ipython()
126 if ip is not None:
126 if ip is not None:
127 return ip.active_eventloop and ip.active_eventloop.startswith('qt')
127 return ip.active_eventloop and ip.active_eventloop.startswith('qt')
128
128
129 # Old way: check attribute on QApplication singleton
129 # Old way: check attribute on QApplication singleton
130 if app is None:
130 if app is None:
131 app = get_app_qt4([''])
131 app = get_app_qt4([''])
132 if hasattr(app, '_in_event_loop'):
132 if hasattr(app, '_in_event_loop'):
133 return app._in_event_loop
133 return app._in_event_loop
134 else:
134 else:
135 # Does qt4 provide a other way to detect this?
135 # Does qt4 provide a other way to detect this?
136 return False
136 return False
137
137
138 def start_event_loop_qt4(app=None):
138 def start_event_loop_qt4(app=None):
139 """Start the qt4 event loop in a consistent manner."""
139 """Start the qt4 event loop in a consistent manner."""
140 if app is None:
140 if app is None:
141 app = get_app_qt4([''])
141 app = get_app_qt4([''])
142 if not is_event_loop_running_qt4(app):
142 if not is_event_loop_running_qt4(app):
143 app._in_event_loop = True
143 app._in_event_loop = True
144 app.exec_()
144 app.exec_()
145 app._in_event_loop = False
145 app._in_event_loop = False
146 else:
146 else:
147 app._in_event_loop = True
147 app._in_event_loop = True
148
148
149 #-----------------------------------------------------------------------------
149 #-----------------------------------------------------------------------------
150 # Tk
150 # Tk
151 #-----------------------------------------------------------------------------
151 #-----------------------------------------------------------------------------
152
152
153 #-----------------------------------------------------------------------------
153 #-----------------------------------------------------------------------------
154 # gtk
154 # gtk
155 #-----------------------------------------------------------------------------
155 #-----------------------------------------------------------------------------
@@ -1,772 +1,772 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with strings and text.
3 Utilities for working with strings and text.
4
4
5 Inheritance diagram:
5 Inheritance diagram:
6
6
7 .. inheritance-diagram:: IPython.utils.text
7 .. inheritance-diagram:: IPython.utils.text
8 :parts: 3
8 :parts: 3
9 """
9 """
10
10
11 import os
11 import os
12 import re
12 import re
13 import sys
13 import sys
14 import textwrap
14 import textwrap
15 from string import Formatter
15 from string import Formatter
16 from pathlib import Path
16 from pathlib import Path
17
17
18 from IPython.utils import py3compat
18 from IPython.utils import py3compat
19
19
20 # datetime.strftime date format for ipython
20 # datetime.strftime date format for ipython
21 if sys.platform == 'win32':
21 if sys.platform == 'win32':
22 date_format = "%B %d, %Y"
22 date_format = "%B %d, %Y"
23 else:
23 else:
24 date_format = "%B %-d, %Y"
24 date_format = "%B %-d, %Y"
25
25
26 class LSString(str):
26 class LSString(str):
27 """String derivative with a special access attributes.
27 """String derivative with a special access attributes.
28
28
29 These are normal strings, but with the special attributes:
29 These are normal strings, but with the special attributes:
30
30
31 .l (or .list) : value as list (split on newlines).
31 .l (or .list) : value as list (split on newlines).
32 .n (or .nlstr): original value (the string itself).
32 .n (or .nlstr): original value (the string itself).
33 .s (or .spstr): value as whitespace-separated string.
33 .s (or .spstr): value as whitespace-separated string.
34 .p (or .paths): list of path objects (requires path.py package)
34 .p (or .paths): list of path objects (requires path.py package)
35
35
36 Any values which require transformations are computed only once and
36 Any values which require transformations are computed only once and
37 cached.
37 cached.
38
38
39 Such strings are very useful to efficiently interact with the shell, which
39 Such strings are very useful to efficiently interact with the shell, which
40 typically only understands whitespace-separated options for commands."""
40 typically only understands whitespace-separated options for commands."""
41
41
42 def get_list(self):
42 def get_list(self):
43 try:
43 try:
44 return self.__list
44 return self.__list
45 except AttributeError:
45 except AttributeError:
46 self.__list = self.split('\n')
46 self.__list = self.split('\n')
47 return self.__list
47 return self.__list
48
48
49 l = list = property(get_list)
49 l = list = property(get_list)
50
50
51 def get_spstr(self):
51 def get_spstr(self):
52 try:
52 try:
53 return self.__spstr
53 return self.__spstr
54 except AttributeError:
54 except AttributeError:
55 self.__spstr = self.replace('\n',' ')
55 self.__spstr = self.replace('\n',' ')
56 return self.__spstr
56 return self.__spstr
57
57
58 s = spstr = property(get_spstr)
58 s = spstr = property(get_spstr)
59
59
60 def get_nlstr(self):
60 def get_nlstr(self):
61 return self
61 return self
62
62
63 n = nlstr = property(get_nlstr)
63 n = nlstr = property(get_nlstr)
64
64
65 def get_paths(self):
65 def get_paths(self):
66 try:
66 try:
67 return self.__paths
67 return self.__paths
68 except AttributeError:
68 except AttributeError:
69 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
69 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
70 return self.__paths
70 return self.__paths
71
71
72 p = paths = property(get_paths)
72 p = paths = property(get_paths)
73
73
74 # FIXME: We need to reimplement type specific displayhook and then add this
74 # FIXME: We need to reimplement type specific displayhook and then add this
75 # back as a custom printer. This should also be moved outside utils into the
75 # back as a custom printer. This should also be moved outside utils into the
76 # core.
76 # core.
77
77
78 # def print_lsstring(arg):
78 # def print_lsstring(arg):
79 # """ Prettier (non-repr-like) and more informative printer for LSString """
79 # """ Prettier (non-repr-like) and more informative printer for LSString """
80 # print "LSString (.p, .n, .l, .s available). Value:"
80 # print "LSString (.p, .n, .l, .s available). Value:"
81 # print arg
81 # print arg
82 #
82 #
83 #
83 #
84 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
84 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
85
85
86
86
87 class SList(list):
87 class SList(list):
88 """List derivative with a special access attributes.
88 """List derivative with a special access attributes.
89
89
90 These are normal lists, but with the special attributes:
90 These are normal lists, but with the special attributes:
91
91
92 * .l (or .list) : value as list (the list itself).
92 * .l (or .list) : value as list (the list itself).
93 * .n (or .nlstr): value as a string, joined on newlines.
93 * .n (or .nlstr): value as a string, joined on newlines.
94 * .s (or .spstr): value as a string, joined on spaces.
94 * .s (or .spstr): value as a string, joined on spaces.
95 * .p (or .paths): list of path objects (requires path.py package)
95 * .p (or .paths): list of path objects (requires path.py package)
96
96
97 Any values which require transformations are computed only once and
97 Any values which require transformations are computed only once and
98 cached."""
98 cached."""
99
99
100 def get_list(self):
100 def get_list(self):
101 return self
101 return self
102
102
103 l = list = property(get_list)
103 l = list = property(get_list)
104
104
105 def get_spstr(self):
105 def get_spstr(self):
106 try:
106 try:
107 return self.__spstr
107 return self.__spstr
108 except AttributeError:
108 except AttributeError:
109 self.__spstr = ' '.join(self)
109 self.__spstr = ' '.join(self)
110 return self.__spstr
110 return self.__spstr
111
111
112 s = spstr = property(get_spstr)
112 s = spstr = property(get_spstr)
113
113
114 def get_nlstr(self):
114 def get_nlstr(self):
115 try:
115 try:
116 return self.__nlstr
116 return self.__nlstr
117 except AttributeError:
117 except AttributeError:
118 self.__nlstr = '\n'.join(self)
118 self.__nlstr = '\n'.join(self)
119 return self.__nlstr
119 return self.__nlstr
120
120
121 n = nlstr = property(get_nlstr)
121 n = nlstr = property(get_nlstr)
122
122
123 def get_paths(self):
123 def get_paths(self):
124 try:
124 try:
125 return self.__paths
125 return self.__paths
126 except AttributeError:
126 except AttributeError:
127 self.__paths = [Path(p) for p in self if os.path.exists(p)]
127 self.__paths = [Path(p) for p in self if os.path.exists(p)]
128 return self.__paths
128 return self.__paths
129
129
130 p = paths = property(get_paths)
130 p = paths = property(get_paths)
131
131
132 def grep(self, pattern, prune = False, field = None):
132 def grep(self, pattern, prune = False, field = None):
133 """ Return all strings matching 'pattern' (a regex or callable)
133 """ Return all strings matching 'pattern' (a regex or callable)
134
134
135 This is case-insensitive. If prune is true, return all items
135 This is case-insensitive. If prune is true, return all items
136 NOT matching the pattern.
136 NOT matching the pattern.
137
137
138 If field is specified, the match must occur in the specified
138 If field is specified, the match must occur in the specified
139 whitespace-separated field.
139 whitespace-separated field.
140
140
141 Examples::
141 Examples::
142
142
143 a.grep( lambda x: x.startswith('C') )
143 a.grep( lambda x: x.startswith('C') )
144 a.grep('Cha.*log', prune=1)
144 a.grep('Cha.*log', prune=1)
145 a.grep('chm', field=-1)
145 a.grep('chm', field=-1)
146 """
146 """
147
147
148 def match_target(s):
148 def match_target(s):
149 if field is None:
149 if field is None:
150 return s
150 return s
151 parts = s.split()
151 parts = s.split()
152 try:
152 try:
153 tgt = parts[field]
153 tgt = parts[field]
154 return tgt
154 return tgt
155 except IndexError:
155 except IndexError:
156 return ""
156 return ""
157
157
158 if isinstance(pattern, str):
158 if isinstance(pattern, str):
159 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
159 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
160 else:
160 else:
161 pred = pattern
161 pred = pattern
162 if not prune:
162 if not prune:
163 return SList([el for el in self if pred(match_target(el))])
163 return SList([el for el in self if pred(match_target(el))])
164 else:
164 else:
165 return SList([el for el in self if not pred(match_target(el))])
165 return SList([el for el in self if not pred(match_target(el))])
166
166
167 def fields(self, *fields):
167 def fields(self, *fields):
168 """ Collect whitespace-separated fields from string list
168 """ Collect whitespace-separated fields from string list
169
169
170 Allows quick awk-like usage of string lists.
170 Allows quick awk-like usage of string lists.
171
171
172 Example data (in var a, created by 'a = !ls -l')::
172 Example data (in var a, created by 'a = !ls -l')::
173
173
174 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
174 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
175 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
175 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
176
176
177 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
177 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
178 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
178 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
179 (note the joining by space).
179 (note the joining by space).
180 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
180 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
181
181
182 IndexErrors are ignored.
182 IndexErrors are ignored.
183
183
184 Without args, fields() just split()'s the strings.
184 Without args, fields() just split()'s the strings.
185 """
185 """
186 if len(fields) == 0:
186 if len(fields) == 0:
187 return [el.split() for el in self]
187 return [el.split() for el in self]
188
188
189 res = SList()
189 res = SList()
190 for el in [f.split() for f in self]:
190 for el in [f.split() for f in self]:
191 lineparts = []
191 lineparts = []
192
192
193 for fd in fields:
193 for fd in fields:
194 try:
194 try:
195 lineparts.append(el[fd])
195 lineparts.append(el[fd])
196 except IndexError:
196 except IndexError:
197 pass
197 pass
198 if lineparts:
198 if lineparts:
199 res.append(" ".join(lineparts))
199 res.append(" ".join(lineparts))
200
200
201 return res
201 return res
202
202
203 def sort(self,field= None, nums = False):
203 def sort(self,field= None, nums = False):
204 """ sort by specified fields (see fields())
204 """ sort by specified fields (see fields())
205
205
206 Example::
206 Example::
207
207
208 a.sort(1, nums = True)
208 a.sort(1, nums = True)
209
209
210 Sorts a by second field, in numerical order (so that 21 > 3)
210 Sorts a by second field, in numerical order (so that 21 > 3)
211
211
212 """
212 """
213
213
214 #decorate, sort, undecorate
214 #decorate, sort, undecorate
215 if field is not None:
215 if field is not None:
216 dsu = [[SList([line]).fields(field), line] for line in self]
216 dsu = [[SList([line]).fields(field), line] for line in self]
217 else:
217 else:
218 dsu = [[line, line] for line in self]
218 dsu = [[line, line] for line in self]
219 if nums:
219 if nums:
220 for i in range(len(dsu)):
220 for i in range(len(dsu)):
221 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
221 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
222 try:
222 try:
223 n = int(numstr)
223 n = int(numstr)
224 except ValueError:
224 except ValueError:
225 n = 0
225 n = 0
226 dsu[i][0] = n
226 dsu[i][0] = n
227
227
228
228
229 dsu.sort()
229 dsu.sort()
230 return SList([t[1] for t in dsu])
230 return SList([t[1] for t in dsu])
231
231
232
232
233 # FIXME: We need to reimplement type specific displayhook and then add this
233 # FIXME: We need to reimplement type specific displayhook and then add this
234 # back as a custom printer. This should also be moved outside utils into the
234 # back as a custom printer. This should also be moved outside utils into the
235 # core.
235 # core.
236
236
237 # def print_slist(arg):
237 # def print_slist(arg):
238 # """ Prettier (non-repr-like) and more informative printer for SList """
238 # """ Prettier (non-repr-like) and more informative printer for SList """
239 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
239 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
240 # if hasattr(arg, 'hideonce') and arg.hideonce:
240 # if hasattr(arg, 'hideonce') and arg.hideonce:
241 # arg.hideonce = False
241 # arg.hideonce = False
242 # return
242 # return
243 #
243 #
244 # nlprint(arg) # This was a nested list printer, now removed.
244 # nlprint(arg) # This was a nested list printer, now removed.
245 #
245 #
246 # print_slist = result_display.when_type(SList)(print_slist)
246 # print_slist = result_display.when_type(SList)(print_slist)
247
247
248
248
249 def indent(instr,nspaces=4, ntabs=0, flatten=False):
249 def indent(instr,nspaces=4, ntabs=0, flatten=False):
250 """Indent a string a given number of spaces or tabstops.
250 """Indent a string a given number of spaces or tabstops.
251
251
252 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
252 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
253
253
254 Parameters
254 Parameters
255 ----------
255 ----------
256
256
257 instr : basestring
257 instr : basestring
258 The string to be indented.
258 The string to be indented.
259 nspaces : int (default: 4)
259 nspaces : int (default: 4)
260 The number of spaces to be indented.
260 The number of spaces to be indented.
261 ntabs : int (default: 0)
261 ntabs : int (default: 0)
262 The number of tabs to be indented.
262 The number of tabs to be indented.
263 flatten : bool (default: False)
263 flatten : bool (default: False)
264 Whether to scrub existing indentation. If True, all lines will be
264 Whether to scrub existing indentation. If True, all lines will be
265 aligned to the same indentation. If False, existing indentation will
265 aligned to the same indentation. If False, existing indentation will
266 be strictly increased.
266 be strictly increased.
267
267
268 Returns
268 Returns
269 -------
269 -------
270
270
271 str|unicode : string indented by ntabs and nspaces.
271 str|unicode : string indented by ntabs and nspaces.
272
272
273 """
273 """
274 if instr is None:
274 if instr is None:
275 return
275 return
276 ind = '\t'*ntabs+' '*nspaces
276 ind = '\t'*ntabs+' '*nspaces
277 if flatten:
277 if flatten:
278 pat = re.compile(r'^\s*', re.MULTILINE)
278 pat = re.compile(r'^\s*', re.MULTILINE)
279 else:
279 else:
280 pat = re.compile(r'^', re.MULTILINE)
280 pat = re.compile(r'^', re.MULTILINE)
281 outstr = re.sub(pat, ind, instr)
281 outstr = re.sub(pat, ind, instr)
282 if outstr.endswith(os.linesep+ind):
282 if outstr.endswith(os.linesep+ind):
283 return outstr[:-len(ind)]
283 return outstr[:-len(ind)]
284 else:
284 else:
285 return outstr
285 return outstr
286
286
287
287
288 def list_strings(arg):
288 def list_strings(arg):
289 """Always return a list of strings, given a string or list of strings
289 """Always return a list of strings, given a string or list of strings
290 as input.
290 as input.
291
291
292 Examples
292 Examples
293 --------
293 --------
294 ::
294 ::
295
295
296 In [7]: list_strings('A single string')
296 In [7]: list_strings('A single string')
297 Out[7]: ['A single string']
297 Out[7]: ['A single string']
298
298
299 In [8]: list_strings(['A single string in a list'])
299 In [8]: list_strings(['A single string in a list'])
300 Out[8]: ['A single string in a list']
300 Out[8]: ['A single string in a list']
301
301
302 In [9]: list_strings(['A','list','of','strings'])
302 In [9]: list_strings(['A','list','of','strings'])
303 Out[9]: ['A', 'list', 'of', 'strings']
303 Out[9]: ['A', 'list', 'of', 'strings']
304 """
304 """
305
305
306 if isinstance(arg, str):
306 if isinstance(arg, str):
307 return [arg]
307 return [arg]
308 else:
308 else:
309 return arg
309 return arg
310
310
311
311
312 def marquee(txt='',width=78,mark='*'):
312 def marquee(txt='',width=78,mark='*'):
313 """Return the input string centered in a 'marquee'.
313 """Return the input string centered in a 'marquee'.
314
314
315 Examples
315 Examples
316 --------
316 --------
317 ::
317 ::
318
318
319 In [16]: marquee('A test',40)
319 In [16]: marquee('A test',40)
320 Out[16]: '**************** A test ****************'
320 Out[16]: '**************** A test ****************'
321
321
322 In [17]: marquee('A test',40,'-')
322 In [17]: marquee('A test',40,'-')
323 Out[17]: '---------------- A test ----------------'
323 Out[17]: '---------------- A test ----------------'
324
324
325 In [18]: marquee('A test',40,' ')
325 In [18]: marquee('A test',40,' ')
326 Out[18]: ' A test '
326 Out[18]: ' A test '
327
327
328 """
328 """
329 if not txt:
329 if not txt:
330 return (mark*width)[:width]
330 return (mark*width)[:width]
331 nmark = (width-len(txt)-2)//len(mark)//2
331 nmark = (width-len(txt)-2)//len(mark)//2
332 if nmark < 0: nmark =0
332 if nmark < 0: nmark =0
333 marks = mark*nmark
333 marks = mark*nmark
334 return '%s %s %s' % (marks,txt,marks)
334 return '%s %s %s' % (marks,txt,marks)
335
335
336
336
337 ini_spaces_re = re.compile(r'^(\s+)')
337 ini_spaces_re = re.compile(r'^(\s+)')
338
338
339 def num_ini_spaces(strng):
339 def num_ini_spaces(strng):
340 """Return the number of initial spaces in a string"""
340 """Return the number of initial spaces in a string"""
341
341
342 ini_spaces = ini_spaces_re.match(strng)
342 ini_spaces = ini_spaces_re.match(strng)
343 if ini_spaces:
343 if ini_spaces:
344 return ini_spaces.end()
344 return ini_spaces.end()
345 else:
345 else:
346 return 0
346 return 0
347
347
348
348
349 def format_screen(strng):
349 def format_screen(strng):
350 """Format a string for screen printing.
350 """Format a string for screen printing.
351
351
352 This removes some latex-type format codes."""
352 This removes some latex-type format codes."""
353 # Paragraph continue
353 # Paragraph continue
354 par_re = re.compile(r'\\$',re.MULTILINE)
354 par_re = re.compile(r'\\$',re.MULTILINE)
355 strng = par_re.sub('',strng)
355 strng = par_re.sub('',strng)
356 return strng
356 return strng
357
357
358
358
359 def dedent(text):
359 def dedent(text):
360 """Equivalent of textwrap.dedent that ignores unindented first line.
360 """Equivalent of textwrap.dedent that ignores unindented first line.
361
361
362 This means it will still dedent strings like:
362 This means it will still dedent strings like:
363 '''foo
363 '''foo
364 is a bar
364 is a bar
365 '''
365 '''
366
366
367 For use in wrap_paragraphs.
367 For use in wrap_paragraphs.
368 """
368 """
369
369
370 if text.startswith('\n'):
370 if text.startswith('\n'):
371 # text starts with blank line, don't ignore the first line
371 # text starts with blank line, don't ignore the first line
372 return textwrap.dedent(text)
372 return textwrap.dedent(text)
373
373
374 # split first line
374 # split first line
375 splits = text.split('\n',1)
375 splits = text.split('\n',1)
376 if len(splits) == 1:
376 if len(splits) == 1:
377 # only one line
377 # only one line
378 return textwrap.dedent(text)
378 return textwrap.dedent(text)
379
379
380 first, rest = splits
380 first, rest = splits
381 # dedent everything but the first line
381 # dedent everything but the first line
382 rest = textwrap.dedent(rest)
382 rest = textwrap.dedent(rest)
383 return '\n'.join([first, rest])
383 return '\n'.join([first, rest])
384
384
385
385
386 def wrap_paragraphs(text, ncols=80):
386 def wrap_paragraphs(text, ncols=80):
387 """Wrap multiple paragraphs to fit a specified width.
387 """Wrap multiple paragraphs to fit a specified width.
388
388
389 This is equivalent to textwrap.wrap, but with support for multiple
389 This is equivalent to textwrap.wrap, but with support for multiple
390 paragraphs, as separated by empty lines.
390 paragraphs, as separated by empty lines.
391
391
392 Returns
392 Returns
393 -------
393 -------
394
394
395 list of complete paragraphs, wrapped to fill `ncols` columns.
395 list of complete paragraphs, wrapped to fill `ncols` columns.
396 """
396 """
397 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
397 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
398 text = dedent(text).strip()
398 text = dedent(text).strip()
399 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
399 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
400 out_ps = []
400 out_ps = []
401 indent_re = re.compile(r'\n\s+', re.MULTILINE)
401 indent_re = re.compile(r'\n\s+', re.MULTILINE)
402 for p in paragraphs:
402 for p in paragraphs:
403 # presume indentation that survives dedent is meaningful formatting,
403 # presume indentation that survives dedent is meaningful formatting,
404 # so don't fill unless text is flush.
404 # so don't fill unless text is flush.
405 if indent_re.search(p) is None:
405 if indent_re.search(p) is None:
406 # wrap paragraph
406 # wrap paragraph
407 p = textwrap.fill(p, ncols)
407 p = textwrap.fill(p, ncols)
408 out_ps.append(p)
408 out_ps.append(p)
409 return out_ps
409 return out_ps
410
410
411
411
412 def long_substr(data):
412 def long_substr(data):
413 """Return the longest common substring in a list of strings.
413 """Return the longest common substring in a list of strings.
414
414
415 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
415 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
416 """
416 """
417 substr = ''
417 substr = ''
418 if len(data) > 1 and len(data[0]) > 0:
418 if len(data) > 1 and len(data[0]) > 0:
419 for i in range(len(data[0])):
419 for i in range(len(data[0])):
420 for j in range(len(data[0])-i+1):
420 for j in range(len(data[0])-i+1):
421 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
421 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
422 substr = data[0][i:i+j]
422 substr = data[0][i:i+j]
423 elif len(data) == 1:
423 elif len(data) == 1:
424 substr = data[0]
424 substr = data[0]
425 return substr
425 return substr
426
426
427
427
428 def strip_email_quotes(text):
428 def strip_email_quotes(text):
429 """Strip leading email quotation characters ('>').
429 """Strip leading email quotation characters ('>').
430
430
431 Removes any combination of leading '>' interspersed with whitespace that
431 Removes any combination of leading '>' interspersed with whitespace that
432 appears *identically* in all lines of the input text.
432 appears *identically* in all lines of the input text.
433
433
434 Parameters
434 Parameters
435 ----------
435 ----------
436 text : str
436 text : str
437
437
438 Examples
438 Examples
439 --------
439 --------
440
440
441 Simple uses::
441 Simple uses::
442
442
443 In [2]: strip_email_quotes('> > text')
443 In [2]: strip_email_quotes('> > text')
444 Out[2]: 'text'
444 Out[2]: 'text'
445
445
446 In [3]: strip_email_quotes('> > text\\n> > more')
446 In [3]: strip_email_quotes('> > text\\n> > more')
447 Out[3]: 'text\\nmore'
447 Out[3]: 'text\\nmore'
448
448
449 Note how only the common prefix that appears in all lines is stripped::
449 Note how only the common prefix that appears in all lines is stripped::
450
450
451 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
451 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
452 Out[4]: '> text\\n> more\\nmore...'
452 Out[4]: '> text\\n> more\\nmore...'
453
453
454 So if any line has no quote marks ('>') , then none are stripped from any
454 So if any line has no quote marks ('>') , then none are stripped from any
455 of them ::
455 of them ::
456
456
457 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
457 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
458 Out[5]: '> > text\\n> > more\\nlast different'
458 Out[5]: '> > text\\n> > more\\nlast different'
459 """
459 """
460 lines = text.splitlines()
460 lines = text.splitlines()
461 matches = set()
461 matches = set()
462 for line in lines:
462 for line in lines:
463 prefix = re.match(r'^(\s*>[ >]*)', line)
463 prefix = re.match(r'^(\s*>[ >]*)', line)
464 if prefix:
464 if prefix:
465 matches.add(prefix.group(1))
465 matches.add(prefix.group(1))
466 else:
466 else:
467 break
467 break
468 else:
468 else:
469 prefix = long_substr(list(matches))
469 prefix = long_substr(list(matches))
470 if prefix:
470 if prefix:
471 strip = len(prefix)
471 strip = len(prefix)
472 text = '\n'.join([ ln[strip:] for ln in lines])
472 text = '\n'.join([ ln[strip:] for ln in lines])
473 return text
473 return text
474
474
475 def strip_ansi(source):
475 def strip_ansi(source):
476 """
476 """
477 Remove ansi escape codes from text.
477 Remove ansi escape codes from text.
478
478
479 Parameters
479 Parameters
480 ----------
480 ----------
481 source : str
481 source : str
482 Source to remove the ansi from
482 Source to remove the ansi from
483 """
483 """
484 return re.sub(r'\033\[(\d|;)+?m', '', source)
484 return re.sub(r'\033\[(\d|;)+?m', '', source)
485
485
486
486
487 class EvalFormatter(Formatter):
487 class EvalFormatter(Formatter):
488 """A String Formatter that allows evaluation of simple expressions.
488 """A String Formatter that allows evaluation of simple expressions.
489
489
490 Note that this version interprets a : as specifying a format string (as per
490 Note that this version interprets a : as specifying a format string (as per
491 standard string formatting), so if slicing is required, you must explicitly
491 standard string formatting), so if slicing is required, you must explicitly
492 create a slice.
492 create a slice.
493
493
494 This is to be used in templating cases, such as the parallel batch
494 This is to be used in templating cases, such as the parallel batch
495 script templates, where simple arithmetic on arguments is useful.
495 script templates, where simple arithmetic on arguments is useful.
496
496
497 Examples
497 Examples
498 --------
498 --------
499 ::
499 ::
500
500
501 In [1]: f = EvalFormatter()
501 In [1]: f = EvalFormatter()
502 In [2]: f.format('{n//4}', n=8)
502 In [2]: f.format('{n//4}', n=8)
503 Out[2]: '2'
503 Out[2]: '2'
504
504
505 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
505 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
506 Out[3]: 'll'
506 Out[3]: 'll'
507 """
507 """
508 def get_field(self, name, args, kwargs):
508 def get_field(self, name, args, kwargs):
509 v = eval(name, kwargs)
509 v = eval(name, kwargs)
510 return v, name
510 return v, name
511
511
512 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
512 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
513 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
513 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
514 # above, it should be possible to remove FullEvalFormatter.
514 # above, it should be possible to remove FullEvalFormatter.
515
515
516 class FullEvalFormatter(Formatter):
516 class FullEvalFormatter(Formatter):
517 """A String Formatter that allows evaluation of simple expressions.
517 """A String Formatter that allows evaluation of simple expressions.
518
518
519 Any time a format key is not found in the kwargs,
519 Any time a format key is not found in the kwargs,
520 it will be tried as an expression in the kwargs namespace.
520 it will be tried as an expression in the kwargs namespace.
521
521
522 Note that this version allows slicing using [1:2], so you cannot specify
522 Note that this version allows slicing using [1:2], so you cannot specify
523 a format string. Use :class:`EvalFormatter` to permit format strings.
523 a format string. Use :class:`EvalFormatter` to permit format strings.
524
524
525 Examples
525 Examples
526 --------
526 --------
527 ::
527 ::
528
528
529 In [1]: f = FullEvalFormatter()
529 In [1]: f = FullEvalFormatter()
530 In [2]: f.format('{n//4}', n=8)
530 In [2]: f.format('{n//4}', n=8)
531 Out[2]: '2'
531 Out[2]: '2'
532
532
533 In [3]: f.format('{list(range(5))[2:4]}')
533 In [3]: f.format('{list(range(5))[2:4]}')
534 Out[3]: '[2, 3]'
534 Out[3]: '[2, 3]'
535
535
536 In [4]: f.format('{3*2}')
536 In [4]: f.format('{3*2}')
537 Out[4]: '6'
537 Out[4]: '6'
538 """
538 """
539 # copied from Formatter._vformat with minor changes to allow eval
539 # copied from Formatter._vformat with minor changes to allow eval
540 # and replace the format_spec code with slicing
540 # and replace the format_spec code with slicing
541 def vformat(self, format_string, args, kwargs):
541 def vformat(self, format_string, args, kwargs):
542 result = []
542 result = []
543 for literal_text, field_name, format_spec, conversion in \
543 for literal_text, field_name, format_spec, conversion in \
544 self.parse(format_string):
544 self.parse(format_string):
545
545
546 # output the literal text
546 # output the literal text
547 if literal_text:
547 if literal_text:
548 result.append(literal_text)
548 result.append(literal_text)
549
549
550 # if there's a field, output it
550 # if there's a field, output it
551 if field_name is not None:
551 if field_name is not None:
552 # this is some markup, find the object and do
552 # this is some markup, find the object and do
553 # the formatting
553 # the formatting
554
554
555 if format_spec:
555 if format_spec:
556 # override format spec, to allow slicing:
556 # override format spec, to allow slicing:
557 field_name = ':'.join([field_name, format_spec])
557 field_name = ':'.join([field_name, format_spec])
558
558
559 # eval the contents of the field for the object
559 # eval the contents of the field for the object
560 # to be formatted
560 # to be formatted
561 obj = eval(field_name, kwargs)
561 obj = eval(field_name, kwargs)
562
562
563 # do any conversion on the resulting object
563 # do any conversion on the resulting object
564 obj = self.convert_field(obj, conversion)
564 obj = self.convert_field(obj, conversion)
565
565
566 # format the object and append to the result
566 # format the object and append to the result
567 result.append(self.format_field(obj, ''))
567 result.append(self.format_field(obj, ''))
568
568
569 return ''.join(py3compat.cast_unicode(s) for s in result)
569 return ''.join(py3compat.cast_unicode(s) for s in result)
570
570
571
571
572 class DollarFormatter(FullEvalFormatter):
572 class DollarFormatter(FullEvalFormatter):
573 """Formatter allowing Itpl style $foo replacement, for names and attribute
573 """Formatter allowing Itpl style $foo replacement, for names and attribute
574 access only. Standard {foo} replacement also works, and allows full
574 access only. Standard {foo} replacement also works, and allows full
575 evaluation of its arguments.
575 evaluation of its arguments.
576
576
577 Examples
577 Examples
578 --------
578 --------
579 ::
579 ::
580
580
581 In [1]: f = DollarFormatter()
581 In [1]: f = DollarFormatter()
582 In [2]: f.format('{n//4}', n=8)
582 In [2]: f.format('{n//4}', n=8)
583 Out[2]: '2'
583 Out[2]: '2'
584
584
585 In [3]: f.format('23 * 76 is $result', result=23*76)
585 In [3]: f.format('23 * 76 is $result', result=23*76)
586 Out[3]: '23 * 76 is 1748'
586 Out[3]: '23 * 76 is 1748'
587
587
588 In [4]: f.format('$a or {b}', a=1, b=2)
588 In [4]: f.format('$a or {b}', a=1, b=2)
589 Out[4]: '1 or 2'
589 Out[4]: '1 or 2'
590 """
590 """
591 _dollar_pattern_ignore_single_quote = re.compile("(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
591 _dollar_pattern_ignore_single_quote = re.compile("(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
592 def parse(self, fmt_string):
592 def parse(self, fmt_string):
593 for literal_txt, field_name, format_spec, conversion \
593 for literal_txt, field_name, format_spec, conversion \
594 in Formatter.parse(self, fmt_string):
594 in Formatter.parse(self, fmt_string):
595
595
596 # Find $foo patterns in the literal text.
596 # Find $foo patterns in the literal text.
597 continue_from = 0
597 continue_from = 0
598 txt = ""
598 txt = ""
599 for m in self._dollar_pattern_ignore_single_quote.finditer(literal_txt):
599 for m in self._dollar_pattern_ignore_single_quote.finditer(literal_txt):
600 new_txt, new_field = m.group(1,2)
600 new_txt, new_field = m.group(1,2)
601 # $$foo --> $foo
601 # $$foo --> $foo
602 if new_field.startswith("$"):
602 if new_field.startswith("$"):
603 txt += new_txt + new_field
603 txt += new_txt + new_field
604 else:
604 else:
605 yield (txt + new_txt, new_field, "", None)
605 yield (txt + new_txt, new_field, "", None)
606 txt = ""
606 txt = ""
607 continue_from = m.end()
607 continue_from = m.end()
608
608
609 # Re-yield the {foo} style pattern
609 # Re-yield the {foo} style pattern
610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
611
611
612 #-----------------------------------------------------------------------------
612 #-----------------------------------------------------------------------------
613 # Utils to columnize a list of string
613 # Utils to columnize a list of string
614 #-----------------------------------------------------------------------------
614 #-----------------------------------------------------------------------------
615
615
616 def _col_chunks(l, max_rows, row_first=False):
616 def _col_chunks(l, max_rows, row_first=False):
617 """Yield successive max_rows-sized column chunks from l."""
617 """Yield successive max_rows-sized column chunks from l."""
618 if row_first:
618 if row_first:
619 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
619 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
620 for i in range(ncols):
620 for i in range(ncols):
621 yield [l[j] for j in range(i, len(l), ncols)]
621 yield [l[j] for j in range(i, len(l), ncols)]
622 else:
622 else:
623 for i in range(0, len(l), max_rows):
623 for i in range(0, len(l), max_rows):
624 yield l[i:(i + max_rows)]
624 yield l[i:(i + max_rows)]
625
625
626
626
627 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
627 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
628 """Calculate optimal info to columnize a list of string"""
628 """Calculate optimal info to columnize a list of string"""
629 for max_rows in range(1, len(rlist) + 1):
629 for max_rows in range(1, len(rlist) + 1):
630 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
630 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
631 sumlength = sum(col_widths)
631 sumlength = sum(col_widths)
632 ncols = len(col_widths)
632 ncols = len(col_widths)
633 if sumlength + separator_size * (ncols - 1) <= displaywidth:
633 if sumlength + separator_size * (ncols - 1) <= displaywidth:
634 break
634 break
635 return {'num_columns': ncols,
635 return {'num_columns': ncols,
636 'optimal_separator_width': (displaywidth - sumlength) // (ncols - 1) if (ncols - 1) else 0,
636 'optimal_separator_width': (displaywidth - sumlength) // (ncols - 1) if (ncols - 1) else 0,
637 'max_rows': max_rows,
637 'max_rows': max_rows,
638 'column_widths': col_widths
638 'column_widths': col_widths
639 }
639 }
640
640
641
641
642 def _get_or_default(mylist, i, default=None):
642 def _get_or_default(mylist, i, default=None):
643 """return list item number, or default if don't exist"""
643 """return list item number, or default if don't exist"""
644 if i >= len(mylist):
644 if i >= len(mylist):
645 return default
645 return default
646 else :
646 else :
647 return mylist[i]
647 return mylist[i]
648
648
649
649
650 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
650 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
651 """Returns a nested list, and info to columnize items
651 """Returns a nested list, and info to columnize items
652
652
653 Parameters
653 Parameters
654 ----------
654 ----------
655
655
656 items
656 items
657 list of strings to columize
657 list of strings to columize
658 row_first : (default False)
658 row_first : (default False)
659 Whether to compute columns for a row-first matrix instead of
659 Whether to compute columns for a row-first matrix instead of
660 column-first (default).
660 column-first (default).
661 empty : (default None)
661 empty : (default None)
662 default value to fill list if needed
662 default value to fill list if needed
663 separator_size : int (default=2)
663 separator_size : int (default=2)
664 How much caracters will be used as a separation between each columns.
664 How much characters will be used as a separation between each columns.
665 displaywidth : int (default=80)
665 displaywidth : int (default=80)
666 The width of the area onto which the columns should enter
666 The width of the area onto which the columns should enter
667
667
668 Returns
668 Returns
669 -------
669 -------
670
670
671 strings_matrix
671 strings_matrix
672
672
673 nested list of string, the outer most list contains as many list as
673 nested list of string, the outer most list contains as many list as
674 rows, the innermost lists have each as many element as columns. If the
674 rows, the innermost lists have each as many element as columns. If the
675 total number of elements in `items` does not equal the product of
675 total number of elements in `items` does not equal the product of
676 rows*columns, the last element of some lists are filled with `None`.
676 rows*columns, the last element of some lists are filled with `None`.
677
677
678 dict_info
678 dict_info
679 some info to make columnize easier:
679 some info to make columnize easier:
680
680
681 num_columns
681 num_columns
682 number of columns
682 number of columns
683 max_rows
683 max_rows
684 maximum number of rows (final number may be less)
684 maximum number of rows (final number may be less)
685 column_widths
685 column_widths
686 list of with of each columns
686 list of with of each columns
687 optimal_separator_width
687 optimal_separator_width
688 best separator width between columns
688 best separator width between columns
689
689
690 Examples
690 Examples
691 --------
691 --------
692 ::
692 ::
693
693
694 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
694 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
695 In [2]: list, info = compute_item_matrix(l, displaywidth=12)
695 In [2]: list, info = compute_item_matrix(l, displaywidth=12)
696 In [3]: list
696 In [3]: list
697 Out[3]: [['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]]
697 Out[3]: [['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]]
698 In [4]: ideal = {'num_columns': 3, 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, 'max_rows': 5}
698 In [4]: ideal = {'num_columns': 3, 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, 'max_rows': 5}
699 In [5]: all((info[k] == ideal[k] for k in ideal.keys()))
699 In [5]: all((info[k] == ideal[k] for k in ideal.keys()))
700 Out[5]: True
700 Out[5]: True
701 """
701 """
702 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
702 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
703 nrow, ncol = info['max_rows'], info['num_columns']
703 nrow, ncol = info['max_rows'], info['num_columns']
704 if row_first:
704 if row_first:
705 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
705 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
706 else:
706 else:
707 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
707 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
708
708
709
709
710 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
710 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
711 """ Transform a list of strings into a single string with columns.
711 """ Transform a list of strings into a single string with columns.
712
712
713 Parameters
713 Parameters
714 ----------
714 ----------
715 items : sequence of strings
715 items : sequence of strings
716 The strings to process.
716 The strings to process.
717
717
718 row_first : (default False)
718 row_first : (default False)
719 Whether to compute columns for a row-first matrix instead of
719 Whether to compute columns for a row-first matrix instead of
720 column-first (default).
720 column-first (default).
721
721
722 separator : str, optional [default is two spaces]
722 separator : str, optional [default is two spaces]
723 The string that separates columns.
723 The string that separates columns.
724
724
725 displaywidth : int, optional [default is 80]
725 displaywidth : int, optional [default is 80]
726 Width of the display in number of characters.
726 Width of the display in number of characters.
727
727
728 Returns
728 Returns
729 -------
729 -------
730 The formatted string.
730 The formatted string.
731 """
731 """
732 if not items:
732 if not items:
733 return '\n'
733 return '\n'
734 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
734 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
735 if spread:
735 if spread:
736 separator = separator.ljust(int(info['optimal_separator_width']))
736 separator = separator.ljust(int(info['optimal_separator_width']))
737 fmatrix = [filter(None, x) for x in matrix]
737 fmatrix = [filter(None, x) for x in matrix]
738 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
738 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
739 return '\n'.join(map(sjoin, fmatrix))+'\n'
739 return '\n'.join(map(sjoin, fmatrix))+'\n'
740
740
741
741
742 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
742 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
743 """
743 """
744 Return a string with a natural enumeration of items
744 Return a string with a natural enumeration of items
745
745
746 >>> get_text_list(['a', 'b', 'c', 'd'])
746 >>> get_text_list(['a', 'b', 'c', 'd'])
747 'a, b, c and d'
747 'a, b, c and d'
748 >>> get_text_list(['a', 'b', 'c'], ' or ')
748 >>> get_text_list(['a', 'b', 'c'], ' or ')
749 'a, b or c'
749 'a, b or c'
750 >>> get_text_list(['a', 'b', 'c'], ', ')
750 >>> get_text_list(['a', 'b', 'c'], ', ')
751 'a, b, c'
751 'a, b, c'
752 >>> get_text_list(['a', 'b'], ' or ')
752 >>> get_text_list(['a', 'b'], ' or ')
753 'a or b'
753 'a or b'
754 >>> get_text_list(['a'])
754 >>> get_text_list(['a'])
755 'a'
755 'a'
756 >>> get_text_list([])
756 >>> get_text_list([])
757 ''
757 ''
758 >>> get_text_list(['a', 'b'], wrap_item_with="`")
758 >>> get_text_list(['a', 'b'], wrap_item_with="`")
759 '`a` and `b`'
759 '`a` and `b`'
760 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
760 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
761 'a + b + c = d'
761 'a + b + c = d'
762 """
762 """
763 if len(list_) == 0:
763 if len(list_) == 0:
764 return ''
764 return ''
765 if wrap_item_with:
765 if wrap_item_with:
766 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
766 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
767 item in list_]
767 item in list_]
768 if len(list_) == 1:
768 if len(list_) == 1:
769 return list_[0]
769 return list_[0]
770 return '%s%s%s' % (
770 return '%s%s%s' % (
771 sep.join(i for i in list_[:-1]),
771 sep.join(i for i in list_[:-1]),
772 last_sep, list_[-1])
772 last_sep, list_[-1])
@@ -1,64 +1,64 b''
1 IPython Documentation
1 IPython Documentation
2 ---------------------
2 ---------------------
3
3
4 This directory contains the majority of the documentation for IPython.
4 This directory contains the majority of the documentation for IPython.
5
5
6
6
7 Deploy docs
7 Deploy docs
8 -----------
8 -----------
9
9
10 Documentation is automatically deployed on ReadTheDocs on every push or merged
10 Documentation is automatically deployed on ReadTheDocs on every push or merged
11 Pull requests.
11 Pull requests.
12
12
13
13
14 Requirements
14 Requirements
15 ------------
15 ------------
16
16
17 The documentation must be built using Python 3.
17 The documentation must be built using Python 3.
18
18
19 In additions to :ref:`devinstall`,
19 In additions to :ref:`devinstall`,
20 the following tools are needed to build the documentation:
20 the following tools are needed to build the documentation:
21
21
22 - sphinx
22 - sphinx
23 - sphinx_rtd_theme
23 - sphinx_rtd_theme
24 - docrepr
24 - docrepr
25
25
26 In a conda environment, or a Python 3 ``venv``, you should be able to run::
26 In a conda environment, or a Python 3 ``venv``, you should be able to run::
27
27
28 cd ipython
28 cd ipython
29 pip install -U -r docs/requirements.txt
29 pip install -U -r docs/requirements.txt
30
30
31
31
32 Build Commands
32 Build Commands
33 --------------
33 --------------
34
34
35 The documentation gets built using ``make``, and comes in several flavors.
35 The documentation gets built using ``make``, and comes in several flavors.
36
36
37 ``make html`` - build the API and narrative documentation web pages, this is
37 ``make html`` - build the API and narrative documentation web pages, this is
38 the default ``make`` target, so running just ``make`` is equivalent to ``make
38 the default ``make`` target, so running just ``make`` is equivalent to ``make
39 html``.
39 html``.
40
40
41 ``make html_noapi`` - same as above, but without running the auto-generated API
41 ``make html_noapi`` - same as above, but without running the auto-generated API
42 docs. When you are working on the narrative documentation, the most time
42 docs. When you are working on the narrative documentation, the most time
43 consuming portion of the build process is the processing and rending of the
43 consuming portion of the build process is the processing and rending of the
44 API documentation. This build target skips that.
44 API documentation. This build target skips that.
45
45
46 ``make pdf`` will compile a pdf from the documentation.
46 ``make pdf`` will compile a pdf from the documentation.
47
47
48 You can run ``make help`` to see information on all possible make targets.
48 You can run ``make help`` to see information on all possible make targets.
49
49
50 To save time,
50 To save time,
51 the make targets above only proceess the files that have been changed since the
51 the make targets above only process the files that have been changed since the
52 previous docs build.
52 previous docs build.
53 To remove the previous docs build you can use ``make clean``.
53 To remove the previous docs build you can use ``make clean``.
54 You can also combine ``clean`` with other `make` commands;
54 You can also combine ``clean`` with other `make` commands;
55 for example,
55 for example,
56 ``make clean html`` will do a complete rebuild of the docs or `make clean pdf` will do a complete build of the pdf.
56 ``make clean html`` will do a complete rebuild of the docs or `make clean pdf` will do a complete build of the pdf.
57
57
58
58
59 Continuous Integration
59 Continuous Integration
60 ----------------------
60 ----------------------
61
61
62 Documentation builds are included in the Travis-CI continuous integration process,
62 Documentation builds are included in the Travis-CI continuous integration process,
63 so you can see the results of the docs build for any pull request at
63 so you can see the results of the docs build for any pull request at
64 https://travis-ci.org/ipython/ipython/pull_requests.
64 https://travis-ci.org/ipython/ipython/pull_requests.
@@ -1,466 +1,466 b''
1 {
1 {
2 "cells": [
2 "cells": [
3 {
3 {
4 "cell_type": "markdown",
4 "cell_type": "markdown",
5 "metadata": {},
5 "metadata": {},
6 "source": [
6 "source": [
7 "# Running Scripts from IPython"
7 "# Running Scripts from IPython"
8 ]
8 ]
9 },
9 },
10 {
10 {
11 "cell_type": "markdown",
11 "cell_type": "markdown",
12 "metadata": {},
12 "metadata": {},
13 "source": [
13 "source": [
14 "IPython has a `%%script` cell magic, which lets you run a cell in\n",
14 "IPython has a `%%script` cell magic, which lets you run a cell in\n",
15 "a subprocess of any interpreter on your system, such as: bash, ruby, perl, zsh, R, etc.\n",
15 "a subprocess of any interpreter on your system, such as: bash, ruby, perl, zsh, R, etc.\n",
16 "\n",
16 "\n",
17 "It can even be a script of your own, which expects input on stdin."
17 "It can even be a script of your own, which expects input on stdin."
18 ]
18 ]
19 },
19 },
20 {
20 {
21 "cell_type": "code",
21 "cell_type": "code",
22 "execution_count": 1,
22 "execution_count": 1,
23 "metadata": {
23 "metadata": {
24 "collapsed": false
24 "collapsed": false
25 },
25 },
26 "outputs": [],
26 "outputs": [],
27 "source": [
27 "source": [
28 "import sys"
28 "import sys"
29 ]
29 ]
30 },
30 },
31 {
31 {
32 "cell_type": "markdown",
32 "cell_type": "markdown",
33 "metadata": {},
33 "metadata": {},
34 "source": [
34 "source": [
35 "## Basic usage"
35 "## Basic usage"
36 ]
36 ]
37 },
37 },
38 {
38 {
39 "cell_type": "markdown",
39 "cell_type": "markdown",
40 "metadata": {},
40 "metadata": {},
41 "source": [
41 "source": [
42 "To use it, simply pass a path or shell command to the program you want to run on the `%%script` line,\n",
42 "To use it, simply pass a path or shell command to the program you want to run on the `%%script` line,\n",
43 "and the rest of the cell will be run by that script, and stdout/err from the subprocess are captured and displayed."
43 "and the rest of the cell will be run by that script, and stdout/err from the subprocess are captured and displayed."
44 ]
44 ]
45 },
45 },
46 {
46 {
47 "cell_type": "code",
47 "cell_type": "code",
48 "execution_count": 2,
48 "execution_count": 2,
49 "metadata": {
49 "metadata": {
50 "collapsed": false
50 "collapsed": false
51 },
51 },
52 "outputs": [
52 "outputs": [
53 {
53 {
54 "name": "stdout",
54 "name": "stdout",
55 "output_type": "stream",
55 "output_type": "stream",
56 "text": [
56 "text": [
57 "hello from Python 2.7.9 (default, Jan 29 2015, 06:27:40) \n",
57 "hello from Python 2.7.9 (default, Jan 29 2015, 06:27:40) \n",
58 "[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)]\n"
58 "[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)]\n"
59 ]
59 ]
60 }
60 }
61 ],
61 ],
62 "source": [
62 "source": [
63 "%%script python2\n",
63 "%%script python2\n",
64 "import sys\n",
64 "import sys\n",
65 "print 'hello from Python %s' % sys.version"
65 "print 'hello from Python %s' % sys.version"
66 ]
66 ]
67 },
67 },
68 {
68 {
69 "cell_type": "code",
69 "cell_type": "code",
70 "execution_count": 3,
70 "execution_count": 3,
71 "metadata": {
71 "metadata": {
72 "collapsed": false
72 "collapsed": false
73 },
73 },
74 "outputs": [
74 "outputs": [
75 {
75 {
76 "name": "stdout",
76 "name": "stdout",
77 "output_type": "stream",
77 "output_type": "stream",
78 "text": [
78 "text": [
79 "hello from Python: 3.4.2 |Continuum Analytics, Inc.| (default, Oct 21 2014, 17:42:20) \n",
79 "hello from Python: 3.4.2 |Continuum Analytics, Inc.| (default, Oct 21 2014, 17:42:20) \n",
80 "[GCC 4.2.1 (Apple Inc. build 5577)]\n"
80 "[GCC 4.2.1 (Apple Inc. build 5577)]\n"
81 ]
81 ]
82 }
82 }
83 ],
83 ],
84 "source": [
84 "source": [
85 "%%script python3\n",
85 "%%script python3\n",
86 "import sys\n",
86 "import sys\n",
87 "print('hello from Python: %s' % sys.version)"
87 "print('hello from Python: %s' % sys.version)"
88 ]
88 ]
89 },
89 },
90 {
90 {
91 "cell_type": "markdown",
91 "cell_type": "markdown",
92 "metadata": {},
92 "metadata": {},
93 "source": [
93 "source": [
94 "IPython also creates aliases for a few common interpreters, such as bash, ruby, perl, etc.\n",
94 "IPython also creates aliases for a few common interpreters, such as bash, ruby, perl, etc.\n",
95 "\n",
95 "\n",
96 "These are all equivalent to `%%script <name>`"
96 "These are all equivalent to `%%script <name>`"
97 ]
97 ]
98 },
98 },
99 {
99 {
100 "cell_type": "code",
100 "cell_type": "code",
101 "execution_count": 4,
101 "execution_count": 4,
102 "metadata": {
102 "metadata": {
103 "collapsed": false
103 "collapsed": false
104 },
104 },
105 "outputs": [
105 "outputs": [
106 {
106 {
107 "name": "stdout",
107 "name": "stdout",
108 "output_type": "stream",
108 "output_type": "stream",
109 "text": [
109 "text": [
110 "Hello from Ruby 2.0.0\n"
110 "Hello from Ruby 2.0.0\n"
111 ]
111 ]
112 }
112 }
113 ],
113 ],
114 "source": [
114 "source": [
115 "%%ruby\n",
115 "%%ruby\n",
116 "puts \"Hello from Ruby #{RUBY_VERSION}\""
116 "puts \"Hello from Ruby #{RUBY_VERSION}\""
117 ]
117 ]
118 },
118 },
119 {
119 {
120 "cell_type": "code",
120 "cell_type": "code",
121 "execution_count": 5,
121 "execution_count": 5,
122 "metadata": {
122 "metadata": {
123 "collapsed": false
123 "collapsed": false
124 },
124 },
125 "outputs": [
125 "outputs": [
126 {
126 {
127 "name": "stdout",
127 "name": "stdout",
128 "output_type": "stream",
128 "output_type": "stream",
129 "text": [
129 "text": [
130 "hello from /usr/local/bin/bash\n"
130 "hello from /usr/local/bin/bash\n"
131 ]
131 ]
132 }
132 }
133 ],
133 ],
134 "source": [
134 "source": [
135 "%%bash\n",
135 "%%bash\n",
136 "echo \"hello from $BASH\""
136 "echo \"hello from $BASH\""
137 ]
137 ]
138 },
138 },
139 {
139 {
140 "cell_type": "markdown",
140 "cell_type": "markdown",
141 "metadata": {},
141 "metadata": {},
142 "source": [
142 "source": [
143 "## Capturing output"
143 "## Capturing output"
144 ]
144 ]
145 },
145 },
146 {
146 {
147 "cell_type": "markdown",
147 "cell_type": "markdown",
148 "metadata": {},
148 "metadata": {},
149 "source": [
149 "source": [
150 "You can also capture stdout/err from these subprocesses into Python variables, instead of letting them go directly to stdout/err"
150 "You can also capture stdout/err from these subprocesses into Python variables, instead of letting them go directly to stdout/err"
151 ]
151 ]
152 },
152 },
153 {
153 {
154 "cell_type": "code",
154 "cell_type": "code",
155 "execution_count": 6,
155 "execution_count": 6,
156 "metadata": {
156 "metadata": {
157 "collapsed": false
157 "collapsed": false
158 },
158 },
159 "outputs": [
159 "outputs": [
160 {
160 {
161 "name": "stdout",
161 "name": "stdout",
162 "output_type": "stream",
162 "output_type": "stream",
163 "text": [
163 "text": [
164 "hi, stdout\n"
164 "hi, stdout\n"
165 ]
165 ]
166 },
166 },
167 {
167 {
168 "name": "stderr",
168 "name": "stderr",
169 "output_type": "stream",
169 "output_type": "stream",
170 "text": [
170 "text": [
171 "hello, stderr\n"
171 "hello, stderr\n"
172 ]
172 ]
173 }
173 }
174 ],
174 ],
175 "source": [
175 "source": [
176 "%%bash\n",
176 "%%bash\n",
177 "echo \"hi, stdout\"\n",
177 "echo \"hi, stdout\"\n",
178 "echo \"hello, stderr\" >&2\n"
178 "echo \"hello, stderr\" >&2\n"
179 ]
179 ]
180 },
180 },
181 {
181 {
182 "cell_type": "code",
182 "cell_type": "code",
183 "execution_count": 7,
183 "execution_count": 7,
184 "metadata": {
184 "metadata": {
185 "collapsed": false
185 "collapsed": false
186 },
186 },
187 "outputs": [],
187 "outputs": [],
188 "source": [
188 "source": [
189 "%%bash --out output --err error\n",
189 "%%bash --out output --err error\n",
190 "echo \"hi, stdout\"\n",
190 "echo \"hi, stdout\"\n",
191 "echo \"hello, stderr\" >&2"
191 "echo \"hello, stderr\" >&2"
192 ]
192 ]
193 },
193 },
194 {
194 {
195 "cell_type": "code",
195 "cell_type": "code",
196 "execution_count": 8,
196 "execution_count": 8,
197 "metadata": {
197 "metadata": {
198 "collapsed": false
198 "collapsed": false
199 },
199 },
200 "outputs": [
200 "outputs": [
201 {
201 {
202 "name": "stdout",
202 "name": "stdout",
203 "output_type": "stream",
203 "output_type": "stream",
204 "text": [
204 "text": [
205 "hello, stderr\n",
205 "hello, stderr\n",
206 "\n",
206 "\n",
207 "hi, stdout\n",
207 "hi, stdout\n",
208 "\n"
208 "\n"
209 ]
209 ]
210 }
210 }
211 ],
211 ],
212 "source": [
212 "source": [
213 "print(error)\n",
213 "print(error)\n",
214 "print(output)"
214 "print(output)"
215 ]
215 ]
216 },
216 },
217 {
217 {
218 "cell_type": "markdown",
218 "cell_type": "markdown",
219 "metadata": {},
219 "metadata": {},
220 "source": [
220 "source": [
221 "## Background Scripts"
221 "## Background Scripts"
222 ]
222 ]
223 },
223 },
224 {
224 {
225 "cell_type": "markdown",
225 "cell_type": "markdown",
226 "metadata": {},
226 "metadata": {},
227 "source": [
227 "source": [
228 "These scripts can be run in the background, by adding the `--bg` flag.\n",
228 "These scripts can be run in the background, by adding the `--bg` flag.\n",
229 "\n",
229 "\n",
230 "When you do this, output is discarded unless you use the `--out/err`\n",
230 "When you do this, output is discarded unless you use the `--out/err`\n",
231 "flags to store output as above."
231 "flags to store output as above."
232 ]
232 ]
233 },
233 },
234 {
234 {
235 "cell_type": "code",
235 "cell_type": "code",
236 "execution_count": 9,
236 "execution_count": 9,
237 "metadata": {
237 "metadata": {
238 "collapsed": false
238 "collapsed": false
239 },
239 },
240 "outputs": [
240 "outputs": [
241 {
241 {
242 "name": "stdout",
242 "name": "stdout",
243 "output_type": "stream",
243 "output_type": "stream",
244 "text": [
244 "text": [
245 "Starting job # 0 in a separate thread.\n"
245 "Starting job # 0 in a separate thread.\n"
246 ]
246 ]
247 }
247 }
248 ],
248 ],
249 "source": [
249 "source": [
250 "%%ruby --bg --out ruby_lines\n",
250 "%%ruby --bg --out ruby_lines\n",
251 "for n in 1...10\n",
251 "for n in 1...10\n",
252 " sleep 1\n",
252 " sleep 1\n",
253 " puts \"line #{n}\"\n",
253 " puts \"line #{n}\"\n",
254 " STDOUT.flush\n",
254 " STDOUT.flush\n",
255 "end"
255 "end"
256 ]
256 ]
257 },
257 },
258 {
258 {
259 "cell_type": "markdown",
259 "cell_type": "markdown",
260 "metadata": {},
260 "metadata": {},
261 "source": [
261 "source": [
262 "When you do store output of a background thread, these are the stdout/err *pipes*,\n",
262 "When you do store output of a background thread, these are the stdout/err *pipes*,\n",
263 "rather than the text of the output."
263 "rather than the text of the output."
264 ]
264 ]
265 },
265 },
266 {
266 {
267 "cell_type": "code",
267 "cell_type": "code",
268 "execution_count": 10,
268 "execution_count": 10,
269 "metadata": {
269 "metadata": {
270 "collapsed": false
270 "collapsed": false
271 },
271 },
272 "outputs": [
272 "outputs": [
273 {
273 {
274 "data": {
274 "data": {
275 "text/plain": [
275 "text/plain": [
276 "<open file '<fdopen>', mode 'rb' at 0x10a4be660>"
276 "<open file '<fdopen>', mode 'rb' at 0x10a4be660>"
277 ]
277 ]
278 },
278 },
279 "execution_count": 10,
279 "execution_count": 10,
280 "metadata": {},
280 "metadata": {},
281 "output_type": "execute_result"
281 "output_type": "execute_result"
282 }
282 }
283 ],
283 ],
284 "source": [
284 "source": [
285 "ruby_lines"
285 "ruby_lines"
286 ]
286 ]
287 },
287 },
288 {
288 {
289 "cell_type": "code",
289 "cell_type": "code",
290 "execution_count": 11,
290 "execution_count": 11,
291 "metadata": {
291 "metadata": {
292 "collapsed": false
292 "collapsed": false
293 },
293 },
294 "outputs": [
294 "outputs": [
295 {
295 {
296 "name": "stdout",
296 "name": "stdout",
297 "output_type": "stream",
297 "output_type": "stream",
298 "text": [
298 "text": [
299 "line 1\n",
299 "line 1\n",
300 "line 2\n",
300 "line 2\n",
301 "line 3\n",
301 "line 3\n",
302 "line 4\n",
302 "line 4\n",
303 "line 5\n",
303 "line 5\n",
304 "line 6\n",
304 "line 6\n",
305 "line 7\n",
305 "line 7\n",
306 "line 8\n",
306 "line 8\n",
307 "line 9\n",
307 "line 9\n",
308 "\n"
308 "\n"
309 ]
309 ]
310 }
310 }
311 ],
311 ],
312 "source": [
312 "source": [
313 "print(ruby_lines.read())"
313 "print(ruby_lines.read())"
314 ]
314 ]
315 },
315 },
316 {
316 {
317 "cell_type": "markdown",
317 "cell_type": "markdown",
318 "metadata": {},
318 "metadata": {},
319 "source": [
319 "source": [
320 "## Arguments to subcommand"
320 "## Arguments to subcommand"
321 ]
321 ]
322 },
322 },
323 {
323 {
324 "cell_type": "markdown",
324 "cell_type": "markdown",
325 "metadata": {},
325 "metadata": {},
326 "source": [
326 "source": [
327 "You can pass arguments the subcommand as well,\n",
327 "You can pass arguments the subcommand as well,\n",
328 "such as this example instructing Python to use integer division from Python 3:"
328 "such as this example instructing Python to use integer division from Python 3:"
329 ]
329 ]
330 },
330 },
331 {
331 {
332 "cell_type": "code",
332 "cell_type": "code",
333 "execution_count": 12,
333 "execution_count": 12,
334 "metadata": {
334 "metadata": {
335 "collapsed": false
335 "collapsed": false
336 },
336 },
337 "outputs": [
337 "outputs": [
338 {
338 {
339 "name": "stdout",
339 "name": "stdout",
340 "output_type": "stream",
340 "output_type": "stream",
341 "text": [
341 "text": [
342 "0.333333333333\n"
342 "0.333333333333\n"
343 ]
343 ]
344 }
344 }
345 ],
345 ],
346 "source": [
346 "source": [
347 "%%script python2 -Qnew\n",
347 "%%script python2 -Qnew\n",
348 "print 1/3"
348 "print 1/3"
349 ]
349 ]
350 },
350 },
351 {
351 {
352 "cell_type": "markdown",
352 "cell_type": "markdown",
353 "metadata": {},
353 "metadata": {},
354 "source": [
354 "source": [
355 "You can really specify *any* program for `%%script`,\n",
355 "You can really specify *any* program for `%%script`,\n",
356 "for instance here is a 'program' that echos the lines of stdin, with delays between each line."
356 "for instance here is a 'program' that echos the lines of stdin, with delays between each line."
357 ]
357 ]
358 },
358 },
359 {
359 {
360 "cell_type": "code",
360 "cell_type": "code",
361 "execution_count": 13,
361 "execution_count": 13,
362 "metadata": {
362 "metadata": {
363 "collapsed": false
363 "collapsed": false
364 },
364 },
365 "outputs": [
365 "outputs": [
366 {
366 {
367 "name": "stdout",
367 "name": "stdout",
368 "output_type": "stream",
368 "output_type": "stream",
369 "text": [
369 "text": [
370 "Starting job # 2 in a separate thread.\n"
370 "Starting job # 2 in a separate thread.\n"
371 ]
371 ]
372 }
372 }
373 ],
373 ],
374 "source": [
374 "source": [
375 "%%script --bg --out bashout bash -c \"while read line; do echo $line; sleep 1; done\"\n",
375 "%%script --bg --out bashout bash -c \"while read line; do echo $line; sleep 1; done\"\n",
376 "line 1\n",
376 "line 1\n",
377 "line 2\n",
377 "line 2\n",
378 "line 3\n",
378 "line 3\n",
379 "line 4\n",
379 "line 4\n",
380 "line 5\n"
380 "line 5\n"
381 ]
381 ]
382 },
382 },
383 {
383 {
384 "cell_type": "markdown",
384 "cell_type": "markdown",
385 "metadata": {},
385 "metadata": {},
386 "source": [
386 "source": [
387 "Remember, since the output of a background script is just the stdout pipe,\n",
387 "Remember, since the output of a background script is just the stdout pipe,\n",
388 "you can read it as lines become available:"
388 "you can read it as lines become available:"
389 ]
389 ]
390 },
390 },
391 {
391 {
392 "cell_type": "code",
392 "cell_type": "code",
393 "execution_count": 14,
393 "execution_count": 14,
394 "metadata": {
394 "metadata": {
395 "collapsed": false
395 "collapsed": false
396 },
396 },
397 "outputs": [
397 "outputs": [
398 {
398 {
399 "name": "stdout",
399 "name": "stdout",
400 "output_type": "stream",
400 "output_type": "stream",
401 "text": [
401 "text": [
402 "0.0s: line 1\n",
402 "0.0s: line 1\n",
403 "1.0s: line 2\n",
403 "1.0s: line 2\n",
404 "2.0s: line 3\n",
404 "2.0s: line 3\n",
405 "3.0s: line 4\n",
405 "3.0s: line 4\n",
406 "4.0s: line 5\n"
406 "4.0s: line 5\n"
407 ]
407 ]
408 }
408 }
409 ],
409 ],
410 "source": [
410 "source": [
411 "import time\n",
411 "import time\n",
412 "tic = time.time()\n",
412 "tic = time.time()\n",
413 "line = True\n",
413 "line = True\n",
414 "while True:\n",
414 "while True:\n",
415 " line = bashout.readline()\n",
415 " line = bashout.readline()\n",
416 " if not line:\n",
416 " if not line:\n",
417 " break\n",
417 " break\n",
418 " sys.stdout.write(\"%.1fs: %s\" %(time.time()-tic, line))\n",
418 " sys.stdout.write(\"%.1fs: %s\" %(time.time()-tic, line))\n",
419 " sys.stdout.flush()\n"
419 " sys.stdout.flush()\n"
420 ]
420 ]
421 },
421 },
422 {
422 {
423 "cell_type": "markdown",
423 "cell_type": "markdown",
424 "metadata": {},
424 "metadata": {},
425 "source": [
425 "source": [
426 "## Configuring the default ScriptMagics"
426 "## Configuring the default ScriptMagics"
427 ]
427 ]
428 },
428 },
429 {
429 {
430 "cell_type": "markdown",
430 "cell_type": "markdown",
431 "metadata": {},
431 "metadata": {},
432 "source": [
432 "source": [
433 "The list of aliased script magics is configurable.\n",
433 "The list of aliased script magics is configurable.\n",
434 "\n",
434 "\n",
435 "The default is to pick from a few common interpreters, and use them if found, but you can specify your own in ipython_config.py:\n",
435 "The default is to pick from a few common interpreters, and use them if found, but you can specify your own in ipython_config.py:\n",
436 "\n",
436 "\n",
437 " c.ScriptMagics.scripts = ['R', 'pypy', 'myprogram']\n",
437 " c.ScriptMagics.scripts = ['R', 'pypy', 'myprogram']\n",
438 "\n",
438 "\n",
439 "And if any of these programs do not apear on your default PATH, then you would also need to specify their location with:\n",
439 "And if any of these programs do not appear on your default PATH, then you would also need to specify their location with:\n",
440 "\n",
440 "\n",
441 " c.ScriptMagics.script_paths = {'myprogram': '/opt/path/to/myprogram'}"
441 " c.ScriptMagics.script_paths = {'myprogram': '/opt/path/to/myprogram'}"
442 ]
442 ]
443 }
443 }
444 ],
444 ],
445 "metadata": {
445 "metadata": {
446 "kernelspec": {
446 "kernelspec": {
447 "display_name": "Python 3",
447 "display_name": "Python 3",
448 "language": "python",
448 "language": "python",
449 "name": "python3"
449 "name": "python3"
450 },
450 },
451 "language_info": {
451 "language_info": {
452 "codemirror_mode": {
452 "codemirror_mode": {
453 "name": "ipython",
453 "name": "ipython",
454 "version": 3
454 "version": 3
455 },
455 },
456 "file_extension": ".py",
456 "file_extension": ".py",
457 "mimetype": "text/x-python",
457 "mimetype": "text/x-python",
458 "name": "python",
458 "name": "python",
459 "nbconvert_exporter": "python",
459 "nbconvert_exporter": "python",
460 "pygments_lexer": "ipython3",
460 "pygments_lexer": "ipython3",
461 "version": "3.4.2"
461 "version": "3.4.2"
462 }
462 }
463 },
463 },
464 "nbformat": 4,
464 "nbformat": 4,
465 "nbformat_minor": 0
465 "nbformat_minor": 0
466 }
466 }
@@ -1,127 +1,127 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """
3 """
4 Usage:
4 Usage:
5 git-mpr [-h] [-l | -a] [pr-number [pr-number ...]]
5 git-mpr [-h] [-l | -a] [pr-number [pr-number ...]]
6
6
7 Type `git mpr -h` for details.
7 Type `git mpr -h` for details.
8 """
8 """
9
9
10
10
11 import io, os
11 import io, os
12 import argparse
12 import argparse
13 from subprocess import check_call, CalledProcessError
13 from subprocess import check_call, CalledProcessError
14
14
15 import gh_api
15 import gh_api
16
16
17 ipy_repository = 'git://github.com/ipython/ipython.git'
17 ipy_repository = 'git://github.com/ipython/ipython.git'
18 gh_project = "ipython/ipython"
18 gh_project = "ipython/ipython"
19 not_merged = {}
19 not_merged = {}
20
20
21 def merge_branch(repo, branch ):
21 def merge_branch(repo, branch ):
22 """try to merge the givent branch into the current one
22 """try to merge the givent branch into the current one
23
23
24 If something does not goes smoothly, merge is aborted
24 If something does not goes smoothly, merge is aborted
25
25
26 Returns True if merge successful, False otherwise
26 Returns True if merge successful, False otherwise
27 """
27 """
28 # Delete the branch first
28 # Delete the branch first
29 try :
29 try :
30 check_call(['git', 'pull', repo, branch], stdin=io.open(os.devnull))
30 check_call(['git', 'pull', repo, branch], stdin=io.open(os.devnull))
31 except CalledProcessError :
31 except CalledProcessError :
32 check_call(['git', 'merge', '--abort'])
32 check_call(['git', 'merge', '--abort'])
33 return False
33 return False
34 return True
34 return True
35
35
36
36
37 def git_new_branch(name):
37 def git_new_branch(name):
38 """Create a new branch with the given name and check it out.
38 """Create a new branch with the given name and check it out.
39 """
39 """
40 check_call(['git', 'checkout', '-b', name])
40 check_call(['git', 'checkout', '-b', name])
41
41
42
42
43 def merge_pr(num):
43 def merge_pr(num):
44 """ try to merge the branch of PR `num` into current branch
44 """ try to merge the branch of PR `num` into current branch
45 """
45 """
46 # Get Github authorisation first, so that the user is prompted straight away
46 # Get Github authorisation first, so that the user is prompted straight away
47 # if their login is needed.
47 # if their login is needed.
48
48
49 pr = gh_api.get_pull_request(gh_project, num)
49 pr = gh_api.get_pull_request(gh_project, num)
50 repo = pr['head']['repo']['clone_url']
50 repo = pr['head']['repo']['clone_url']
51
51
52
52
53 branch = pr['head']['ref']
53 branch = pr['head']['ref']
54 mergeable = merge_branch(repo=repo,
54 mergeable = merge_branch(repo=repo,
55 branch=branch,
55 branch=branch,
56 )
56 )
57 if not mergeable :
57 if not mergeable :
58 cmd = "git pull "+repo+" "+branch
58 cmd = "git pull "+repo+" "+branch
59 not_merged[str(num)] = cmd
59 not_merged[str(num)] = cmd
60 print("==============================================================================")
60 print("==============================================================================")
61 print("Something went wrong merging this branch, you can try it manually by runngin :")
61 print("Something went wrong merging this branch, you can try it manually by runngin :")
62 print(cmd)
62 print(cmd)
63 print("==============================================================================")
63 print("==============================================================================")
64
64
65
65
66 def main(*args):
66 def main(*args):
67 parser = argparse.ArgumentParser(
67 parser = argparse.ArgumentParser(
68 description="""
68 description="""
69 Merge one or more github pull requests by their number. If any
69 Merge one or more github pull requests by their number. If any
70 one pull request can't be merged as is, its merge is ignored
70 one pull request can't be merged as is, its merge is ignored
71 and the process continues with the next ones (if any).
71 and the process continues with the next ones (if any).
72 """
72 """
73 )
73 )
74
74
75 grp = parser.add_mutually_exclusive_group()
75 grp = parser.add_mutually_exclusive_group()
76 grp.add_argument(
76 grp.add_argument(
77 '-l',
77 '-l',
78 '--list',
78 '--list',
79 action='store_const',
79 action='store_const',
80 const=True,
80 const=True,
81 help='list PR, their number and their mergeability')
81 help='list PR, their number and their mergeability')
82 grp.add_argument('-a',
82 grp.add_argument('-a',
83 '--merge-all',
83 '--merge-all',
84 action='store_const',
84 action='store_const',
85 const=True ,
85 const=True ,
86 help='try to merge as many PR as possible, one by one')
86 help='try to merge as many PR as possible, one by one')
87 parser.add_argument('merge',
87 parser.add_argument('merge',
88 type=int,
88 type=int,
89 help="The pull request numbers",
89 help="The pull request numbers",
90 nargs='*',
90 nargs='*',
91 metavar='pr-number')
91 metavar='pr-number')
92 args = parser.parse_args()
92 args = parser.parse_args()
93
93
94 if(args.list):
94 if(args.list):
95 pr_list = gh_api.get_pulls_list(gh_project)
95 pr_list = gh_api.get_pulls_list(gh_project)
96 for pr in pr_list :
96 for pr in pr_list :
97 mergeable = gh_api.get_pull_request(gh_project, pr['number'])['mergeable']
97 mergeable = gh_api.get_pull_request(gh_project, pr['number'])['mergeable']
98
98
99 ismgb = u"√" if mergeable else " "
99 ismgb = u"√" if mergeable else " "
100 print(u"* #{number} [{ismgb}]: {title}".format(
100 print(u"* #{number} [{ismgb}]: {title}".format(
101 number=pr['number'],
101 number=pr['number'],
102 title=pr['title'],
102 title=pr['title'],
103 ismgb=ismgb))
103 ismgb=ismgb))
104
104
105 if(args.merge_all):
105 if(args.merge_all):
106 branch_name = 'merge-' + '-'.join(str(pr['number']) for pr in pr_list)
106 branch_name = 'merge-' + '-'.join(str(pr['number']) for pr in pr_list)
107 git_new_branch(branch_name)
107 git_new_branch(branch_name)
108 pr_list = gh_api.get_pulls_list(gh_project)
108 pr_list = gh_api.get_pulls_list(gh_project)
109 for pr in pr_list :
109 for pr in pr_list :
110 merge_pr(pr['number'])
110 merge_pr(pr['number'])
111
111
112
112
113 elif args.merge:
113 elif args.merge:
114 branch_name = 'merge-' + '-'.join(map(str, args.merge))
114 branch_name = 'merge-' + '-'.join(map(str, args.merge))
115 git_new_branch(branch_name)
115 git_new_branch(branch_name)
116 for num in args.merge :
116 for num in args.merge :
117 merge_pr(num)
117 merge_pr(num)
118
118
119 if not_merged :
119 if not_merged :
120 print('*************************************************************************************')
120 print('*************************************************************************************')
121 print('the following branch have not been merged automatically, considere doing it by hand :')
121 print('The following branch has not been merged automatically, consider doing it by hand :')
122 for num, cmd in not_merged.items() :
122 for num, cmd in not_merged.items() :
123 print( "PR {num}: {cmd}".format(num=num, cmd=cmd))
123 print( "PR {num}: {cmd}".format(num=num, cmd=cmd))
124 print('*************************************************************************************')
124 print('*************************************************************************************')
125
125
126 if __name__ == '__main__':
126 if __name__ == '__main__':
127 main()
127 main()
General Comments 0
You need to be logged in to leave comments. Login now