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