##// END OF EJS Templates
correct Demo namespace in documentation
pepie34 -
Show More
@@ -1,667 +1,667 b''
1 1 """Module for interactive demos using IPython.
2 2
3 3 This module implements a few classes for running Python scripts interactively
4 4 in IPython for demonstrations. With very simple markup (a few tags in
5 5 comments), you can control points where the script stops executing and returns
6 6 control to IPython.
7 7
8 8
9 9 Provided classes
10 10 ----------------
11 11
12 12 The classes are (see their docstrings for further details):
13 13
14 14 - Demo: pure python demos
15 15
16 16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 17 typed interactively (so magics work, as well as any other special syntax you
18 18 may have added via input prefilters).
19 19
20 20 - LineDemo: single-line version of the Demo class. These demos are executed
21 21 one line at a time, and require no markup.
22 22
23 23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 24 executed a line at a time, but processed via IPython).
25 25
26 26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 27 declares an empty marquee and a pre_cmd that clears the screen before each
28 28 block (see Subclassing below).
29 29
30 30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 31 classes.
32 32
33 33 Inheritance diagram:
34 34
35 35 .. inheritance-diagram:: IPython.lib.demo
36 36 :parts: 3
37 37
38 38 Subclassing
39 39 -----------
40 40
41 41 The classes here all include a few methods meant to make customization by
42 42 subclassing more convenient. Their docstrings below have some more details:
43 43
44 44 - highlight(): format every block and optionally highlight comments and
45 45 docstring content.
46 46
47 47 - marquee(): generates a marquee to provide visible on-screen markers at each
48 48 block start and end.
49 49
50 50 - pre_cmd(): run right before the execution of each block.
51 51
52 52 - post_cmd(): run right after the execution of each block. If the block
53 53 raises an exception, this is NOT called.
54 54
55 55
56 56 Operation
57 57 ---------
58 58
59 59 The file is run in its own empty namespace (though you can pass it a string of
60 60 arguments as if in a command line environment, and it will see those as
61 61 sys.argv). But at each stop, the global IPython namespace is updated with the
62 62 current internal demo namespace, so you can work interactively with the data
63 63 accumulated so far.
64 64
65 65 By default, each block of code is printed (with syntax highlighting) before
66 66 executing it and you have to confirm execution. This is intended to show the
67 67 code to an audience first so you can discuss it, and only proceed with
68 68 execution once you agree. There are a few tags which allow you to modify this
69 69 behavior.
70 70
71 71 The supported tags are:
72 72
73 73 # <demo> stop
74 74
75 75 Defines block boundaries, the points where IPython stops execution of the
76 76 file and returns to the interactive prompt.
77 77
78 78 You can optionally mark the stop tag with extra dashes before and after the
79 79 word 'stop', to help visually distinguish the blocks in a text editor:
80 80
81 81 # <demo> --- stop ---
82 82
83 83
84 84 # <demo> silent
85 85
86 86 Make a block execute silently (and hence automatically). Typically used in
87 87 cases where you have some boilerplate or initialization code which you need
88 88 executed but do not want to be seen in the demo.
89 89
90 90 # <demo> auto
91 91
92 92 Make a block execute automatically, but still being printed. Useful for
93 93 simple code which does not warrant discussion, since it avoids the extra
94 94 manual confirmation.
95 95
96 96 # <demo> auto_all
97 97
98 98 This tag can _only_ be in the first block, and if given it overrides the
99 99 individual auto tags to make the whole demo fully automatic (no block asks
100 100 for confirmation). It can also be given at creation time (or the attribute
101 101 set later) to override what's in the file.
102 102
103 103 While _any_ python file can be run as a Demo instance, if there are no stop
104 104 tags the whole file will run in a single block (no different that calling
105 105 first %pycat and then %run). The minimal markup to make this useful is to
106 106 place a set of stop tags; the other tags are only there to let you fine-tune
107 107 the execution.
108 108
109 109 This is probably best explained with the simple example file below. You can
110 110 copy this into a file named ex_demo.py, and try running it via::
111 111
112 from IPython.demo import Demo
112 from IPython.lib.demo import Demo
113 113 d = Demo('ex_demo.py')
114 114 d()
115 115
116 116 Each time you call the demo object, it runs the next block. The demo object
117 117 has a few useful methods for navigation, like again(), edit(), jump(), seek()
118 118 and back(). It can be reset for a new run via reset() or reloaded from disk
119 119 (in case you've edited the source) via reload(). See their docstrings below.
120 120
121 121 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
122 122 been added to the "docs/examples/core" directory. Just cd to this directory in
123 123 an IPython session, and type::
124 124
125 125 %run demo-exercizer.py
126 126
127 127 and then follow the directions.
128 128
129 129 Example
130 130 -------
131 131
132 132 The following is a very simple example of a valid demo file.
133 133
134 134 ::
135 135
136 136 #################### EXAMPLE DEMO <ex_demo.py> ###############################
137 137 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
138 138
139 139 print 'Hello, welcome to an interactive IPython demo.'
140 140
141 141 # The mark below defines a block boundary, which is a point where IPython will
142 142 # stop execution and return to the interactive prompt. The dashes are actually
143 143 # optional and used only as a visual aid to clearly separate blocks while
144 144 # editing the demo code.
145 145 # <demo> stop
146 146
147 147 x = 1
148 148 y = 2
149 149
150 150 # <demo> stop
151 151
152 152 # the mark below makes this block as silent
153 153 # <demo> silent
154 154
155 155 print 'This is a silent block, which gets executed but not printed.'
156 156
157 157 # <demo> stop
158 158 # <demo> auto
159 159 print 'This is an automatic block.'
160 160 print 'It is executed without asking for confirmation, but printed.'
161 161 z = x+y
162 162
163 163 print 'z=',x
164 164
165 165 # <demo> stop
166 166 # This is just another normal block.
167 167 print 'z is now:', z
168 168
169 169 print 'bye!'
170 170 ################### END EXAMPLE DEMO <ex_demo.py> ############################
171 171 """
172 172
173 173
174 174 #*****************************************************************************
175 175 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
176 176 #
177 177 # Distributed under the terms of the BSD License. The full license is in
178 178 # the file COPYING, distributed as part of this software.
179 179 #
180 180 #*****************************************************************************
181 181
182 182 import os
183 183 import re
184 184 import shlex
185 185 import sys
186 186 import pygments
187 187
188 188 from IPython.utils.text import marquee
189 189 from IPython.utils import openpy
190 190 from IPython.utils import py3compat
191 191 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
192 192
193 193 class DemoError(Exception): pass
194 194
195 195 def re_mark(mark):
196 196 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
197 197
198 198 class Demo(object):
199 199
200 200 re_stop = re_mark('-*\s?stop\s?-*')
201 201 re_silent = re_mark('silent')
202 202 re_auto = re_mark('auto')
203 203 re_auto_all = re_mark('auto_all')
204 204
205 205 def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False,
206 206 formatter='terminal', style='default'):
207 207 """Make a new demo object. To run the demo, simply call the object.
208 208
209 209 See the module docstring for full details and an example (you can use
210 210 IPython.Demo? in IPython to see it).
211 211
212 212 Inputs:
213 213
214 214 - src is either a file, or file-like object, or a
215 215 string that can be resolved to a filename.
216 216
217 217 Optional inputs:
218 218
219 219 - title: a string to use as the demo name. Of most use when the demo
220 220 you are making comes from an object that has no filename, or if you
221 221 want an alternate denotation distinct from the filename.
222 222
223 223 - arg_str(''): a string of arguments, internally converted to a list
224 224 just like sys.argv, so the demo script can see a similar
225 225 environment.
226 226
227 227 - auto_all(None): global flag to run all blocks automatically without
228 228 confirmation. This attribute overrides the block-level tags and
229 229 applies to the whole demo. It is an attribute of the object, and
230 230 can be changed at runtime simply by reassigning it to a boolean
231 231 value.
232 232
233 233 - format_rst(False): a bool to enable comments and doc strings
234 234 formating with pygments rst lexer
235 235
236 236 - formatter('terminal'): a string of pygments formatter name to be
237 237 used. Useful values for terminals: terminal, terminal256,
238 238 terminal16m
239 239
240 240 - style('default'): a string of pygments style name to be used.
241 241 """
242 242 if hasattr(src, "read"):
243 243 # It seems to be a file or a file-like object
244 244 self.fname = "from a file-like object"
245 245 if title == '':
246 246 self.title = "from a file-like object"
247 247 else:
248 248 self.title = title
249 249 else:
250 250 # Assume it's a string or something that can be converted to one
251 251 self.fname = src
252 252 if title == '':
253 253 (filepath, filename) = os.path.split(src)
254 254 self.title = filename
255 255 else:
256 256 self.title = title
257 257 self.sys_argv = [src] + shlex.split(arg_str)
258 258 self.auto_all = auto_all
259 259 self.src = src
260 260
261 261 self.inside_ipython = "get_ipython" in globals()
262 262 if self.inside_ipython:
263 263 # get a few things from ipython. While it's a bit ugly design-wise,
264 264 # it ensures that things like color scheme and the like are always in
265 265 # sync with the ipython mode being used. This class is only meant to
266 266 # be used inside ipython anyways, so it's OK.
267 267 ip = get_ipython() # this is in builtins whenever IPython is running
268 268 self.ip_ns = ip.user_ns
269 269 self.ip_colorize = ip.pycolorize
270 270 self.ip_showtb = ip.showtraceback
271 271 self.ip_run_cell = ip.run_cell
272 272 self.shell = ip
273 273
274 274 self.formatter = pygments.formatters.get_formatter_by_name(formatter,
275 275 style=style)
276 276 self.python_lexer = pygments.lexers.get_lexer_by_name("py3")
277 277 self.format_rst = format_rst
278 278 if format_rst:
279 279 self.rst_lexer = pygments.lexers.get_lexer_by_name("rst")
280 280
281 281 # load user data and initialize data structures
282 282 self.reload()
283 283
284 284 def fload(self):
285 285 """Load file object."""
286 286 # read data and parse into blocks
287 287 if hasattr(self, 'fobj') and self.fobj is not None:
288 288 self.fobj.close()
289 289 if hasattr(self.src, "read"):
290 290 # It seems to be a file or a file-like object
291 291 self.fobj = self.src
292 292 else:
293 293 # Assume it's a string or something that can be converted to one
294 294 self.fobj = openpy.open(self.fname)
295 295
296 296 def reload(self):
297 297 """Reload source from disk and initialize state."""
298 298 self.fload()
299 299
300 300 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
301 301 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
302 302 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
303 303 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
304 304
305 305 # if auto_all is not given (def. None), we read it from the file
306 306 if self.auto_all is None:
307 307 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
308 308 else:
309 309 self.auto_all = bool(self.auto_all)
310 310
311 311 # Clean the sources from all markup so it doesn't get displayed when
312 312 # running the demo
313 313 src_blocks = []
314 314 auto_strip = lambda s: self.re_auto.sub('',s)
315 315 for i,b in enumerate(src_b):
316 316 if self._auto[i]:
317 317 src_blocks.append(auto_strip(b))
318 318 else:
319 319 src_blocks.append(b)
320 320 # remove the auto_all marker
321 321 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
322 322
323 323 self.nblocks = len(src_blocks)
324 324 self.src_blocks = src_blocks
325 325
326 326 # also build syntax-highlighted source
327 327 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
328 328
329 329 # ensure clean namespace and seek offset
330 330 self.reset()
331 331
332 332 def reset(self):
333 333 """Reset the namespace and seek pointer to restart the demo"""
334 334 self.user_ns = {}
335 335 self.finished = False
336 336 self.block_index = 0
337 337
338 338 def _validate_index(self,index):
339 339 if index<0 or index>=self.nblocks:
340 340 raise ValueError('invalid block index %s' % index)
341 341
342 342 def _get_index(self,index):
343 343 """Get the current block index, validating and checking status.
344 344
345 345 Returns None if the demo is finished"""
346 346
347 347 if index is None:
348 348 if self.finished:
349 349 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
350 350 return None
351 351 index = self.block_index
352 352 else:
353 353 self._validate_index(index)
354 354 return index
355 355
356 356 def seek(self,index):
357 357 """Move the current seek pointer to the given block.
358 358
359 359 You can use negative indices to seek from the end, with identical
360 360 semantics to those of Python lists."""
361 361 if index<0:
362 362 index = self.nblocks + index
363 363 self._validate_index(index)
364 364 self.block_index = index
365 365 self.finished = False
366 366
367 367 def back(self,num=1):
368 368 """Move the seek pointer back num blocks (default is 1)."""
369 369 self.seek(self.block_index-num)
370 370
371 371 def jump(self,num=1):
372 372 """Jump a given number of blocks relative to the current one.
373 373
374 374 The offset can be positive or negative, defaults to 1."""
375 375 self.seek(self.block_index+num)
376 376
377 377 def again(self):
378 378 """Move the seek pointer back one block and re-execute."""
379 379 self.back(1)
380 380 self()
381 381
382 382 def edit(self,index=None):
383 383 """Edit a block.
384 384
385 385 If no number is given, use the last block executed.
386 386
387 387 This edits the in-memory copy of the demo, it does NOT modify the
388 388 original source file. If you want to do that, simply open the file in
389 389 an editor and use reload() when you make changes to the file. This
390 390 method is meant to let you change a block during a demonstration for
391 391 explanatory purposes, without damaging your original script."""
392 392
393 393 index = self._get_index(index)
394 394 if index is None:
395 395 return
396 396 # decrease the index by one (unless we're at the very beginning), so
397 397 # that the default demo.edit() call opens up the sblock we've last run
398 398 if index>0:
399 399 index -= 1
400 400
401 401 filename = self.shell.mktempfile(self.src_blocks[index])
402 402 self.shell.hooks.editor(filename,1)
403 403 with open(filename, 'r') as f:
404 404 new_block = f.read()
405 405 # update the source and colored block
406 406 self.src_blocks[index] = new_block
407 407 self.src_blocks_colored[index] = self.highlight(new_block)
408 408 self.block_index = index
409 409 # call to run with the newly edited index
410 410 self()
411 411
412 412 def show(self,index=None):
413 413 """Show a single block on screen"""
414 414
415 415 index = self._get_index(index)
416 416 if index is None:
417 417 return
418 418
419 419 print(self.marquee('<%s> block # %s (%s remaining)' %
420 420 (self.title,index,self.nblocks-index-1)))
421 421 print(self.src_blocks_colored[index])
422 422 sys.stdout.flush()
423 423
424 424 def show_all(self):
425 425 """Show entire demo on screen, block by block"""
426 426
427 427 fname = self.title
428 428 title = self.title
429 429 nblocks = self.nblocks
430 430 silent = self._silent
431 431 marquee = self.marquee
432 432 for index,block in enumerate(self.src_blocks_colored):
433 433 if silent[index]:
434 434 print(marquee('<%s> SILENT block # %s (%s remaining)' %
435 435 (title,index,nblocks-index-1)))
436 436 else:
437 437 print(marquee('<%s> block # %s (%s remaining)' %
438 438 (title,index,nblocks-index-1)))
439 439 print(block, end=' ')
440 440 sys.stdout.flush()
441 441
442 442 def run_cell(self,source):
443 443 """Execute a string with one or more lines of code"""
444 444
445 445 exec(source, self.user_ns)
446 446
447 447 def __call__(self,index=None):
448 448 """run a block of the demo.
449 449
450 450 If index is given, it should be an integer >=1 and <= nblocks. This
451 451 means that the calling convention is one off from typical Python
452 452 lists. The reason for the inconsistency is that the demo always
453 453 prints 'Block n/N, and N is the total, so it would be very odd to use
454 454 zero-indexing here."""
455 455
456 456 index = self._get_index(index)
457 457 if index is None:
458 458 return
459 459 try:
460 460 marquee = self.marquee
461 461 next_block = self.src_blocks[index]
462 462 self.block_index += 1
463 463 if self._silent[index]:
464 464 print(marquee('Executing silent block # %s (%s remaining)' %
465 465 (index,self.nblocks-index-1)))
466 466 else:
467 467 self.pre_cmd()
468 468 self.show(index)
469 469 if self.auto_all or self._auto[index]:
470 470 print(marquee('output:'))
471 471 else:
472 472 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
473 473 ans = py3compat.input().strip()
474 474 if ans:
475 475 print(marquee('Block NOT executed'))
476 476 return
477 477 try:
478 478 save_argv = sys.argv
479 479 sys.argv = self.sys_argv
480 480 self.run_cell(next_block)
481 481 self.post_cmd()
482 482 finally:
483 483 sys.argv = save_argv
484 484
485 485 except:
486 486 if self.inside_ipython:
487 487 self.ip_showtb(filename=self.fname)
488 488 else:
489 489 if self.inside_ipython:
490 490 self.ip_ns.update(self.user_ns)
491 491
492 492 if self.block_index == self.nblocks:
493 493 mq1 = self.marquee('END OF DEMO')
494 494 if mq1:
495 495 # avoid spurious print if empty marquees are used
496 496 print()
497 497 print(mq1)
498 498 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
499 499 self.finished = True
500 500
501 501 # These methods are meant to be overridden by subclasses who may wish to
502 502 # customize the behavior of of their demos.
503 503 def marquee(self,txt='',width=78,mark='*'):
504 504 """Return the input string centered in a 'marquee'."""
505 505 return marquee(txt,width,mark)
506 506
507 507 def pre_cmd(self):
508 508 """Method called before executing each block."""
509 509 pass
510 510
511 511 def post_cmd(self):
512 512 """Method called after executing each block."""
513 513 pass
514 514
515 515 def highlight(self, block):
516 516 """Method called on each block to highlight it content"""
517 517 tokens = pygments.lex(block, self.python_lexer)
518 518 if self.format_rst:
519 519 from pygments.token import Token
520 520 toks = []
521 521 for token in tokens:
522 522 if token[0] == Token.String.Doc and len(token[1]) > 6:
523 523 toks += pygments.lex(token[1][:3], self.python_lexer)
524 524 # parse doc string content by rst lexer
525 525 toks += pygments.lex(token[1][3:-3], self.rst_lexer)
526 526 toks += pygments.lex(token[1][-3:], self.python_lexer)
527 527 elif token[0] == Token.Comment.Single:
528 528 toks.append((Token.Comment.Single, token[1][0]))
529 529 # parse comment content by rst lexer
530 530 # remove the extrat newline added by rst lexer
531 531 toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1]
532 532 else:
533 533 toks.append(token)
534 534 tokens = toks
535 535 return pygments.format(tokens, self.formatter)
536 536
537 537
538 538 class IPythonDemo(Demo):
539 539 """Class for interactive demos with IPython's input processing applied.
540 540
541 541 This subclasses Demo, but instead of executing each block by the Python
542 542 interpreter (via exec), it actually calls IPython on it, so that any input
543 543 filters which may be in place are applied to the input block.
544 544
545 545 If you have an interactive environment which exposes special input
546 546 processing, you can use this class instead to write demo scripts which
547 547 operate exactly as if you had typed them interactively. The default Demo
548 548 class requires the input to be valid, pure Python code.
549 549 """
550 550
551 551 def run_cell(self,source):
552 552 """Execute a string with one or more lines of code"""
553 553
554 554 self.shell.run_cell(source)
555 555
556 556 class LineDemo(Demo):
557 557 """Demo where each line is executed as a separate block.
558 558
559 559 The input script should be valid Python code.
560 560
561 561 This class doesn't require any markup at all, and it's meant for simple
562 562 scripts (with no nesting or any kind of indentation) which consist of
563 563 multiple lines of input to be executed, one at a time, as if they had been
564 564 typed in the interactive prompt.
565 565
566 566 Note: the input can not have *any* indentation, which means that only
567 567 single-lines of input are accepted, not even function definitions are
568 568 valid."""
569 569
570 570 def reload(self):
571 571 """Reload source from disk and initialize state."""
572 572 # read data and parse into blocks
573 573 self.fload()
574 574 lines = self.fobj.readlines()
575 575 src_b = [l for l in lines if l.strip()]
576 576 nblocks = len(src_b)
577 577 self.src = ''.join(lines)
578 578 self._silent = [False]*nblocks
579 579 self._auto = [True]*nblocks
580 580 self.auto_all = True
581 581 self.nblocks = nblocks
582 582 self.src_blocks = src_b
583 583
584 584 # also build syntax-highlighted source
585 585 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
586 586
587 587 # ensure clean namespace and seek offset
588 588 self.reset()
589 589
590 590
591 591 class IPythonLineDemo(IPythonDemo,LineDemo):
592 592 """Variant of the LineDemo class whose input is processed by IPython."""
593 593 pass
594 594
595 595
596 596 class ClearMixin(object):
597 597 """Use this mixin to make Demo classes with less visual clutter.
598 598
599 599 Demos using this mixin will clear the screen before every block and use
600 600 blank marquees.
601 601
602 602 Note that in order for the methods defined here to actually override those
603 603 of the classes it's mixed with, it must go /first/ in the inheritance
604 604 tree. For example:
605 605
606 606 class ClearIPDemo(ClearMixin,IPythonDemo): pass
607 607
608 608 will provide an IPythonDemo class with the mixin's features.
609 609 """
610 610
611 611 def marquee(self,txt='',width=78,mark='*'):
612 612 """Blank marquee that returns '' no matter what the input."""
613 613 return ''
614 614
615 615 def pre_cmd(self):
616 616 """Method called before executing each block.
617 617
618 618 This one simply clears the screen."""
619 619 from IPython.utils.terminal import _term_clear
620 620 _term_clear()
621 621
622 622 class ClearDemo(ClearMixin,Demo):
623 623 pass
624 624
625 625
626 626 class ClearIPDemo(ClearMixin,IPythonDemo):
627 627 pass
628 628
629 629
630 630 def slide(file_path, noclear=False, format_rst=True, formatter="terminal",
631 631 style="native", auto_all=False, delimiter='...'):
632 632 if noclear:
633 633 demo_class = Demo
634 634 else:
635 635 demo_class = ClearDemo
636 636 demo = demo_class(file_path, format_rst=format_rst, formatter=formatter,
637 637 style=style, auto_all=auto_all)
638 638 while not demo.finished:
639 639 demo()
640 640 try:
641 641 py3compat.input('\n' + delimiter)
642 642 except KeyboardInterrupt:
643 643 exit(1)
644 644
645 645 if __name__ == '__main__':
646 646 import argparse
647 647 parser = argparse.ArgumentParser(description='Run python demos')
648 648 parser.add_argument('--noclear', '-C', action='store_true',
649 649 help='Do not clear terminal on each slide')
650 650 parser.add_argument('--rst', '-r', action='store_true',
651 651 help='Highlight comments and dostrings as rst')
652 652 parser.add_argument('--formatter', '-f', default='terminal',
653 653 help='pygments formatter name could be: terminal, '
654 654 'terminal256, terminal16m')
655 655 parser.add_argument('--style', '-s', default='default',
656 656 help='pygments style name')
657 657 parser.add_argument('--auto', '-a', action='store_true',
658 658 help='Run all blocks automatically without'
659 659 'confirmation')
660 660 parser.add_argument('--delimiter', '-d', default='...',
661 661 help='slides delimiter added after each slide run')
662 662 parser.add_argument('file', nargs=1,
663 663 help='python demo file')
664 664 args = parser.parse_args()
665 665 slide(args.file[0], noclear=args.noclear, format_rst=args.rst,
666 666 formatter=args.formatter, style=args.style, auto_all=args.auto,
667 667 delimiter=args.delimiter)
General Comments 0
You need to be logged in to leave comments. Login now