##// END OF EJS Templates
Merging -r 1178 from lp:ipython....
Brian Granger -
r2125:2a3afbbd merge
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 """This is meant to be run from the IPython prompt:
2
3 %run demo-exercizer.py
4
5 It will create demo objects of the example that is embedded in demo.py in a
6 number of ways and allow you to see how they work, just follow the printed
7 directions."""
8
9 #-----------------------------------------------------------------------------
10 # Imports
11 #-----------------------------------------------------------------------------
12
13 # From std lib
14 import StringIO
15 import os
16 import shutil
17 import tempfile
18
19 # From IPython
20 from IPython.demo import (Demo, IPythonDemo, LineDemo, IPythonLineDemo,
21 ClearDemo, ClearIPDemo)
22
23 #-----------------------------------------------------------------------------
24 # Demo code
25 #-----------------------------------------------------------------------------
26
27 example1 = """
28 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
29
30 print 'Hello, welcome to an interactive IPython demo.'
31
32 # The mark below defines a block boundary, which is a point where IPython will
33 # stop execution and return to the interactive prompt. The dashes are actually
34 # optional and used only as a visual aid to clearly separate blocks while
35 # editing the demo code.
36 # <demo> stop
37
38 x = 1
39 y = 2
40
41 # <demo> stop
42
43 # the mark below makes this block as silent
44 # <demo> silent
45
46 print 'This is a silent block, which gets executed but not printed.'
47
48 # <demo> stop
49 # <demo> auto
50 print 'This is an automatic block.'
51 print 'It is executed without asking for confirmation, but printed.'
52 z = x+y
53
54 print 'z=',x
55
56 # <demo> stop
57 # This is just another normal block.
58 print 'z is now:', z
59
60 print 'bye!'
61 """
62 fp = tempfile.mkdtemp(prefix = 'DemoTmp')
63 fd, filename = tempfile.mkstemp(prefix = 'demoExample1File', suffix = '.py',
64 dir = fp)
65 f = os.fdopen(fd, 'wt')
66
67 f.write(example1)
68 f.close()
69
70 my_d = Demo(filename)
71 my_cd = ClearDemo(filename)
72
73 fobj = StringIO.StringIO(example1)
74 str_d = Demo(fobj, title='via stringio')
75
76 print '''
77 The example that is embeded in demo.py file has been used to create
78 the following 3 demos, and should now be available to use:
79 my_d() -- created from a file
80 my_cd() -- created from a file, a ClearDemo
81 str_d() -- same as above, but created via a StringIO object
82 Call by typing their name, (with parentheses), at the
83 ipython prompt, interact with the block, then call again
84 to run the next block.
85 '''
@@ -1,526 +1,554 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
34 34 Subclassing
35 35 ===========
36 36
37 37 The classes here all include a few methods meant to make customization by
38 38 subclassing more convenient. Their docstrings below have some more details:
39 39
40 40 - marquee(): generates a marquee to provide visible on-screen markers at each
41 41 block start and end.
42 42
43 43 - pre_cmd(): run right before the execution of each block.
44 44
45 45 - post_cmd(): run right after the execution of each block. If the block
46 46 raises an exception, this is NOT called.
47 47
48 48
49 49 Operation
50 50 =========
51 51
52 52 The file is run in its own empty namespace (though you can pass it a string of
53 53 arguments as if in a command line environment, and it will see those as
54 54 sys.argv). But at each stop, the global IPython namespace is updated with the
55 55 current internal demo namespace, so you can work interactively with the data
56 56 accumulated so far.
57 57
58 58 By default, each block of code is printed (with syntax highlighting) before
59 59 executing it and you have to confirm execution. This is intended to show the
60 60 code to an audience first so you can discuss it, and only proceed with
61 61 execution once you agree. There are a few tags which allow you to modify this
62 62 behavior.
63 63
64 64 The supported tags are:
65 65
66 66 # <demo> stop
67 67
68 68 Defines block boundaries, the points where IPython stops execution of the
69 69 file and returns to the interactive prompt.
70 70
71 71 You can optionally mark the stop tag with extra dashes before and after the
72 72 word 'stop', to help visually distinguish the blocks in a text editor:
73 73
74 74 # <demo> --- stop ---
75 75
76 76
77 77 # <demo> silent
78 78
79 79 Make a block execute silently (and hence automatically). Typically used in
80 80 cases where you have some boilerplate or initialization code which you need
81 81 executed but do not want to be seen in the demo.
82 82
83 83 # <demo> auto
84 84
85 85 Make a block execute automatically, but still being printed. Useful for
86 86 simple code which does not warrant discussion, since it avoids the extra
87 87 manual confirmation.
88 88
89 89 # <demo> auto_all
90 90
91 91 This tag can _only_ be in the first block, and if given it overrides the
92 92 individual auto tags to make the whole demo fully automatic (no block asks
93 93 for confirmation). It can also be given at creation time (or the attribute
94 94 set later) to override what's in the file.
95 95
96 96 While _any_ python file can be run as a Demo instance, if there are no stop
97 97 tags the whole file will run in a single block (no different that calling
98 98 first %pycat and then %run). The minimal markup to make this useful is to
99 99 place a set of stop tags; the other tags are only there to let you fine-tune
100 100 the execution.
101 101
102 102 This is probably best explained with the simple example file below. You can
103 103 copy this into a file named ex_demo.py, and try running it via:
104 104
105 105 from IPython.demo import Demo
106 106 d = Demo('ex_demo.py')
107 107 d() <--- Call the d object (omit the parens if you have autocall set to 2).
108 108
109 109 Each time you call the demo object, it runs the next block. The demo object
110 110 has a few useful methods for navigation, like again(), edit(), jump(), seek()
111 111 and back(). It can be reset for a new run via reset() or reloaded from disk
112 112 (in case you've edited the source) via reload(). See their docstrings below.
113 113
114 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
115 been added to the "docs/examples/core" directory. Just cd to this directory in
116 an IPython session, and type::
117
118 %run demo-exercizer.py
119
120 and then follow the directions.
114 121
115 122 Example
116 123 =======
117 124
118 125 The following is a very simple example of a valid demo file.
119 126
120 127 #################### EXAMPLE DEMO <ex_demo.py> ###############################
121 128 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
122 129
123 130 print 'Hello, welcome to an interactive IPython demo.'
124 131
125 132 # The mark below defines a block boundary, which is a point where IPython will
126 133 # stop execution and return to the interactive prompt. The dashes are actually
127 134 # optional and used only as a visual aid to clearly separate blocks while
128 editing the demo code.
135 # editing the demo code.
129 136 # <demo> stop
130 137
131 138 x = 1
132 139 y = 2
133 140
134 141 # <demo> stop
135 142
136 143 # the mark below makes this block as silent
137 144 # <demo> silent
138 145
139 146 print 'This is a silent block, which gets executed but not printed.'
140 147
141 148 # <demo> stop
142 149 # <demo> auto
143 150 print 'This is an automatic block.'
144 151 print 'It is executed without asking for confirmation, but printed.'
145 152 z = x+y
146 153
147 154 print 'z=',x
148 155
149 156 # <demo> stop
150 157 # This is just another normal block.
151 158 print 'z is now:', z
152 159
153 160 print 'bye!'
154 161 ################### END EXAMPLE DEMO <ex_demo.py> ############################
155 162 """
156 163
157 164 #*****************************************************************************
158 165 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
159 166 #
160 167 # Distributed under the terms of the BSD License. The full license is in
161 168 # the file COPYING, distributed as part of this software.
162 169 #
163 170 #*****************************************************************************
164 171
165 172 import exceptions
166 173 import os
167 174 import re
168 175 import shlex
169 176 import sys
170 177
171 178 from IPython.utils.PyColorize import Parser
172 from IPython.utils.genutils import marquee, file_read, file_readlines
179 from IPython.utils.genutils import marquee, file_read, file_readlines, Term
173 180
174 181 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
175 182
176 183 class DemoError(exceptions.Exception): pass
177 184
178 185 def re_mark(mark):
179 186 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
180 187
181 188 class Demo(object):
182 189
183 190 re_stop = re_mark('-*\s?stop\s?-*')
184 191 re_silent = re_mark('silent')
185 192 re_auto = re_mark('auto')
186 193 re_auto_all = re_mark('auto_all')
187 194
188 def __init__(self,fname,arg_str='',auto_all=None):
195 def __init__(self,src,title='',arg_str='',auto_all=None):
189 196 """Make a new demo object. To run the demo, simply call the object.
190 197
191 198 See the module docstring for full details and an example (you can use
192 199 IPython.Demo? in IPython to see it).
193 200
194 201 Inputs:
195 202
196 - fname = filename.
203 - src is either a file, or file-like object, or a
204 string that can be resolved to a filename.
197 205
198 206 Optional inputs:
199 207
208 - title: a string to use as the demo name. Of most use when the demo
209 you are making comes from an object that has no filename, or if you
210 want an alternate denotation distinct from the filename.
211
200 212 - arg_str(''): a string of arguments, internally converted to a list
201 213 just like sys.argv, so the demo script can see a similar
202 214 environment.
203 215
204 216 - auto_all(None): global flag to run all blocks automatically without
205 217 confirmation. This attribute overrides the block-level tags and
206 218 applies to the whole demo. It is an attribute of the object, and
207 219 can be changed at runtime simply by reassigning it to a boolean
208 220 value.
209 221 """
210
211 self.fname = fname
212 self.sys_argv = [fname] + shlex.split(arg_str)
222 if hasattr(src, "read"):
223 # It seems to be a file or a file-like object
224 self.fobj = src
225 self.fname = "from a file-like object"
226 if title == '':
227 self.title = "from a file-like object"
228 else:
229 self.title = title
230 else:
231 # Assume it's a string or something that can be converted to one
232 self.fobj = open(src)
233 self.fname = src
234 if title == '':
235 (filepath, filename) = os.path.split(src)
236 self.title = filename
237 else:
238 self.title = title
239 self.sys_argv = [src] + shlex.split(arg_str)
213 240 self.auto_all = auto_all
214 241
215 242 # get a few things from ipython. While it's a bit ugly design-wise,
216 243 # it ensures that things like color scheme and the like are always in
217 244 # sync with the ipython mode being used. This class is only meant to
218 245 # be used inside ipython anyways, so it's OK.
219 246 self.ip_ns = __IPYTHON__.user_ns
220 247 self.ip_colorize = __IPYTHON__.pycolorize
221 248 self.ip_showtb = __IPYTHON__.showtraceback
222 249 self.ip_runlines = __IPYTHON__.runlines
223 250 self.shell = __IPYTHON__
224 251
225 252 # load user data and initialize data structures
226 253 self.reload()
227 254
228 255 def reload(self):
229 256 """Reload source from disk and initialize state."""
230 257 # read data and parse into blocks
231 self.src = file_read(self.fname)
258 self.src = self.fobj.read()
232 259 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
233 260 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
234 261 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
235 262
236 263 # if auto_all is not given (def. None), we read it from the file
237 264 if self.auto_all is None:
238 265 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
239 266 else:
240 267 self.auto_all = bool(self.auto_all)
241 268
242 269 # Clean the sources from all markup so it doesn't get displayed when
243 270 # running the demo
244 271 src_blocks = []
245 272 auto_strip = lambda s: self.re_auto.sub('',s)
246 273 for i,b in enumerate(src_b):
247 274 if self._auto[i]:
248 275 src_blocks.append(auto_strip(b))
249 276 else:
250 277 src_blocks.append(b)
251 278 # remove the auto_all marker
252 279 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
253 280
254 281 self.nblocks = len(src_blocks)
255 282 self.src_blocks = src_blocks
256 283
257 284 # also build syntax-highlighted source
258 285 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
259 286
260 287 # ensure clean namespace and seek offset
261 288 self.reset()
262 289
263 290 def reset(self):
264 291 """Reset the namespace and seek pointer to restart the demo"""
265 292 self.user_ns = {}
266 293 self.finished = False
267 294 self.block_index = 0
268 295
269 296 def _validate_index(self,index):
270 297 if index<0 or index>=self.nblocks:
271 298 raise ValueError('invalid block index %s' % index)
272 299
273 300 def _get_index(self,index):
274 301 """Get the current block index, validating and checking status.
275 302
276 303 Returns None if the demo is finished"""
277 304
278 305 if index is None:
279 306 if self.finished:
280 print 'Demo finished. Use reset() if you want to rerun it.'
307 print >>Term.cout, 'Demo finished. Use <demo_name>.reset() if you want to rerun it.'
281 308 return None
282 309 index = self.block_index
283 310 else:
284 311 self._validate_index(index)
285 312 return index
286 313
287 314 def seek(self,index):
288 315 """Move the current seek pointer to the given block.
289 316
290 317 You can use negative indices to seek from the end, with identical
291 318 semantics to those of Python lists."""
292 319 if index<0:
293 320 index = self.nblocks + index
294 321 self._validate_index(index)
295 322 self.block_index = index
296 323 self.finished = False
297 324
298 325 def back(self,num=1):
299 326 """Move the seek pointer back num blocks (default is 1)."""
300 327 self.seek(self.block_index-num)
301 328
302 329 def jump(self,num=1):
303 330 """Jump a given number of blocks relative to the current one.
304 331
305 332 The offset can be positive or negative, defaults to 1."""
306 333 self.seek(self.block_index+num)
307 334
308 335 def again(self):
309 336 """Move the seek pointer back one block and re-execute."""
310 337 self.back(1)
311 338 self()
312 339
313 340 def edit(self,index=None):
314 341 """Edit a block.
315 342
316 343 If no number is given, use the last block executed.
317 344
318 345 This edits the in-memory copy of the demo, it does NOT modify the
319 346 original source file. If you want to do that, simply open the file in
320 347 an editor and use reload() when you make changes to the file. This
321 348 method is meant to let you change a block during a demonstration for
322 349 explanatory purposes, without damaging your original script."""
323 350
324 351 index = self._get_index(index)
325 352 if index is None:
326 353 return
327 354 # decrease the index by one (unless we're at the very beginning), so
328 355 # that the default demo.edit() call opens up the sblock we've last run
329 356 if index>0:
330 357 index -= 1
331 358
332 359 filename = self.shell.mktempfile(self.src_blocks[index])
333 360 self.shell.hooks.editor(filename,1)
334 361 new_block = file_read(filename)
335 362 # update the source and colored block
336 363 self.src_blocks[index] = new_block
337 364 self.src_blocks_colored[index] = self.ip_colorize(new_block)
338 365 self.block_index = index
339 366 # call to run with the newly edited index
340 367 self()
341 368
342 369 def show(self,index=None):
343 370 """Show a single block on screen"""
344 371
345 372 index = self._get_index(index)
346 373 if index is None:
347 374 return
348 375
349 print self.marquee('<%s> block # %s (%s remaining)' %
350 (self.fname,index,self.nblocks-index-1))
351 sys.stdout.write(self.src_blocks_colored[index])
376 print >>Term.cout, self.marquee('<%s> block # %s (%s remaining)' %
377 (self.title,index,self.nblocks-index-1))
378 print >>Term.cout,(self.src_blocks_colored[index])
352 379 sys.stdout.flush()
353 380
354 381 def show_all(self):
355 382 """Show entire demo on screen, block by block"""
356 383
357 fname = self.fname
384 fname = self.title
385 title = self.title
358 386 nblocks = self.nblocks
359 387 silent = self._silent
360 388 marquee = self.marquee
361 389 for index,block in enumerate(self.src_blocks_colored):
362 390 if silent[index]:
363 print marquee('<%s> SILENT block # %s (%s remaining)' %
364 (fname,index,nblocks-index-1))
391 print >>Term.cout, marquee('<%s> SILENT block # %s (%s remaining)' %
392 (title,index,nblocks-index-1))
365 393 else:
366 print marquee('<%s> block # %s (%s remaining)' %
367 (fname,index,nblocks-index-1))
368 print block,
394 print >>Term.cout, marquee('<%s> block # %s (%s remaining)' %
395 (title,index,nblocks-index-1))
396 print >>Term.cout, block,
369 397 sys.stdout.flush()
370 398
371 399 def runlines(self,source):
372 400 """Execute a string with one or more lines of code"""
373 401
374 402 exec source in self.user_ns
375 403
376 404 def __call__(self,index=None):
377 405 """run a block of the demo.
378 406
379 407 If index is given, it should be an integer >=1 and <= nblocks. This
380 408 means that the calling convention is one off from typical Python
381 409 lists. The reason for the inconsistency is that the demo always
382 410 prints 'Block n/N, and N is the total, so it would be very odd to use
383 411 zero-indexing here."""
384 412
385 413 index = self._get_index(index)
386 414 if index is None:
387 415 return
388 416 try:
389 417 marquee = self.marquee
390 418 next_block = self.src_blocks[index]
391 419 self.block_index += 1
392 420 if self._silent[index]:
393 print marquee('Executing silent block # %s (%s remaining)' %
421 print >>Term.cout, marquee('Executing silent block # %s (%s remaining)' %
394 422 (index,self.nblocks-index-1))
395 423 else:
396 424 self.pre_cmd()
397 425 self.show(index)
398 426 if self.auto_all or self._auto[index]:
399 print marquee('output:')
427 print >>Term.cout, marquee('output:')
400 428 else:
401 print marquee('Press <q> to quit, <Enter> to execute...'),
429 print >>Term.cout, marquee('Press <q> to quit, <Enter> to execute...'),
402 430 ans = raw_input().strip()
403 431 if ans:
404 print marquee('Block NOT executed')
432 print >>Term.cout, marquee('Block NOT executed')
405 433 return
406 434 try:
407 435 save_argv = sys.argv
408 436 sys.argv = self.sys_argv
409 437 self.runlines(next_block)
410 438 self.post_cmd()
411 439 finally:
412 440 sys.argv = save_argv
413 441
414 442 except:
415 443 self.ip_showtb(filename=self.fname)
416 444 else:
417 445 self.ip_ns.update(self.user_ns)
418 446
419 447 if self.block_index == self.nblocks:
420 448 mq1 = self.marquee('END OF DEMO')
421 449 if mq1:
422 # avoid spurious prints if empty marquees are used
423 print
424 print mq1
425 print self.marquee('Use reset() if you want to rerun it.')
450 # avoid spurious print >>Term.cout,s if empty marquees are used
451 print >>Term.cout
452 print >>Term.cout, mq1
453 print >>Term.cout, self.marquee('Use <demo_name>.reset() if you want to rerun it.')
426 454 self.finished = True
427 455
428 456 # These methods are meant to be overridden by subclasses who may wish to
429 457 # customize the behavior of of their demos.
430 458 def marquee(self,txt='',width=78,mark='*'):
431 459 """Return the input string centered in a 'marquee'."""
432 460 return marquee(txt,width,mark)
433 461
434 462 def pre_cmd(self):
435 463 """Method called before executing each block."""
436 464 pass
437 465
438 466 def post_cmd(self):
439 467 """Method called after executing each block."""
440 468 pass
441 469
442 470
443 471 class IPythonDemo(Demo):
444 472 """Class for interactive demos with IPython's input processing applied.
445 473
446 474 This subclasses Demo, but instead of executing each block by the Python
447 475 interpreter (via exec), it actually calls IPython on it, so that any input
448 476 filters which may be in place are applied to the input block.
449 477
450 478 If you have an interactive environment which exposes special input
451 479 processing, you can use this class instead to write demo scripts which
452 480 operate exactly as if you had typed them interactively. The default Demo
453 481 class requires the input to be valid, pure Python code.
454 482 """
455 483
456 484 def runlines(self,source):
457 485 """Execute a string with one or more lines of code"""
458 486
459 487 self.shell.runlines(source)
460 488
461 489 class LineDemo(Demo):
462 490 """Demo where each line is executed as a separate block.
463 491
464 492 The input script should be valid Python code.
465 493
466 494 This class doesn't require any markup at all, and it's meant for simple
467 495 scripts (with no nesting or any kind of indentation) which consist of
468 496 multiple lines of input to be executed, one at a time, as if they had been
469 497 typed in the interactive prompt."""
470 498
471 499 def reload(self):
472 500 """Reload source from disk and initialize state."""
473 501 # read data and parse into blocks
474 src_b = [l for l in file_readlines(self.fname) if l.strip()]
502 src_b = [l for l in self.fobj.readline() if l.strip()]
475 503 nblocks = len(src_b)
476 self.src = os.linesep.join(file_readlines(self.fname))
504 self.src = os.linesep.join(self.fobj.readlines())
477 505 self._silent = [False]*nblocks
478 506 self._auto = [True]*nblocks
479 507 self.auto_all = True
480 508 self.nblocks = nblocks
481 509 self.src_blocks = src_b
482 510
483 511 # also build syntax-highlighted source
484 512 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
485 513
486 514 # ensure clean namespace and seek offset
487 515 self.reset()
488 516
489 517
490 518 class IPythonLineDemo(IPythonDemo,LineDemo):
491 519 """Variant of the LineDemo class whose input is processed by IPython."""
492 520 pass
493 521
494 522
495 523 class ClearMixin(object):
496 524 """Use this mixin to make Demo classes with less visual clutter.
497 525
498 526 Demos using this mixin will clear the screen before every block and use
499 527 blank marquees.
500 528
501 529 Note that in order for the methods defined here to actually override those
502 530 of the classes it's mixed with, it must go /first/ in the inheritance
503 531 tree. For example:
504 532
505 533 class ClearIPDemo(ClearMixin,IPythonDemo): pass
506 534
507 535 will provide an IPythonDemo class with the mixin's features.
508 536 """
509 537
510 538 def marquee(self,txt='',width=78,mark='*'):
511 539 """Blank marquee that returns '' no matter what the input."""
512 540 return ''
513 541
514 542 def pre_cmd(self):
515 543 """Method called before executing each block.
516 544
517 545 This one simply clears the screen."""
518 os.system('clear')
519
546 from IPython.utils.platutils import term_clear
547 term_clear()
520 548
521 549 class ClearDemo(ClearMixin,Demo):
522 550 pass
523 551
524 552
525 553 class ClearIPDemo(ClearMixin,IPythonDemo):
526 554 pass
@@ -1,106 +1,109 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ Proxy module for accessing platform specific utility functions.
3 3
4 4 Importing this module should give you the implementations that are correct
5 5 for your operation system, from platutils_PLATFORMNAME module.
6 6 """
7 7
8 8 #*****************************************************************************
9 9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #*****************************************************************************
14 14
15 15 import os
16 16 import sys
17 17 import warnings
18 18
19 19 # Import the platform-specific implementations
20 20 if os.name == 'posix':
21 21 import platutils_posix as _platutils
22 22 elif sys.platform == 'win32':
23 23 import platutils_win32 as _platutils
24 24 else:
25 25 import platutils_dummy as _platutils
26 26 import warnings
27 27 warnings.warn("Platutils not available for platform '%s', some features may be missing" %
28 28 os.name)
29 29 del warnings
30 30
31 31
32 32 # Functionality that's logically common to all platforms goes here, each
33 33 # platform-specific module only provides the bits that are OS-dependent.
34 34
35 35 # XXX - I'm still not happy with a module global for this, but at least now
36 36 # there is a public, cross-platform way of toggling the term title control on
37 37 # and off. We should make this a stateful object later on so that each user
38 38 # can have its own instance if needed.
39 def term_clear():
40 _platutils.term_clear()
41
39 42 def toggle_set_term_title(val):
40 43 """Control whether set_term_title is active or not.
41 44
42 45 set_term_title() allows writing to the console titlebar. In embedded
43 46 widgets this can cause problems, so this call can be used to toggle it on
44 47 or off as needed.
45 48
46 49 The default state of the module is for the function to be disabled.
47 50
48 51 Parameters
49 52 ----------
50 53 val : bool
51 54 If True, set_term_title() actually writes to the terminal (using the
52 55 appropriate platform-specific module). If False, it is a no-op.
53 56 """
54 57 _platutils.ignore_termtitle = not(val)
55 58
56 59
57 60 def set_term_title(title):
58 61 """Set terminal title using the necessary platform-dependent calls."""
59 62
60 63 if _platutils.ignore_termtitle:
61 64 return
62 65 _platutils.set_term_title(title)
63 66
64 67
65 68 class FindCmdError(Exception):
66 69 pass
67 70
68 71 def find_cmd(cmd):
69 72 """Find full path to executable cmd in a cross platform manner.
70 73
71 74 This function tries to determine the full path to a command line program
72 75 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
73 76 time it will use the version that is first on the users `PATH`. If
74 77 cmd is `python` return `sys.executable`.
75 78
76 79 Parameters
77 80 ----------
78 81 cmd : str
79 82 The command line program to look for.
80 83 """
81 84 if cmd == 'python':
82 85 return sys.executable
83 86 try:
84 87 path = _platutils.find_cmd(cmd)
85 88 except:
86 89 raise FindCmdError('command could not be found: %s' % cmd)
87 90 # which returns empty if not found
88 91 if path == '':
89 92 raise FindCmdError('command could not be found: %s' % cmd)
90 93 return path
91 94
92 95 def get_long_path_name(path):
93 96 """Expand a path into its long form.
94 97
95 98 On Windows this expands any ~ in the paths. On other platforms, it is
96 99 a null operation.
97 100 """
98 101 return _platutils.get_long_path_name(path)
99 102
100 103 #-----------------------------------------------------------------------------
101 104 # Deprecated functions
102 105 #-----------------------------------------------------------------------------
103 106 def freeze_term_title():
107 import warnings
104 108 warnings.warn("This function is deprecated, use toggle_set_term_title()")
105 109 _platutils.ignore_termtitle = True
106
@@ -1,40 +1,47 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ Platform specific utility functions, posix version
3 3
4 4 Importing this module directly is not portable - rather, import platutils
5 5 to use these functions in platform agnostic fashion.
6 6 """
7 7
8 8 #*****************************************************************************
9 9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #*****************************************************************************
14 14
15 15 import sys
16 16 import os
17 17
18 18 ignore_termtitle = True
19 19
20 20 def _dummy_op(*a, **b):
21 21 """ A no-op function """
22 22
23
23 24 def _set_term_title_xterm(title):
24 25 """ Change virtual terminal title in xterm-workalikes """
25 26
26 27 sys.stdout.write('\033]0;%s\007' % title)
27 28
28 29
29 30 if os.environ.get('TERM','') == 'xterm':
30 31 set_term_title = _set_term_title_xterm
31 32 else:
32 33 set_term_title = _dummy_op
33 34
35
34 36 def find_cmd(cmd):
35 37 """Find the full path to a command using which."""
36 38 return os.popen('which %s' % cmd).read().strip()
37 39
40
38 41 def get_long_path_name(path):
39 42 """Dummy no-op."""
40 43 return path
44
45
46 def term_clear():
47 os.system('clear')
@@ -1,82 +1,87 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ Platform specific utility functions, win32 version
3 3
4 4 Importing this module directly is not portable - rather, import platutils
5 5 to use these functions in platform agnostic fashion.
6 6 """
7 7
8 8 #*****************************************************************************
9 9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #*****************************************************************************
14 14
15 15 import os
16 16
17 17 ignore_termtitle = True
18 18
19 19 try:
20 20 import ctypes
21 21
22 22 SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW
23 23 SetConsoleTitleW.argtypes = [ctypes.c_wchar_p]
24 24
25 25 def set_term_title(title):
26 26 """Set terminal title using ctypes to access the Win32 APIs."""
27 27 SetConsoleTitleW(title)
28 28
29 29 except ImportError:
30 30 def set_term_title(title):
31 31 """Set terminal title using the 'title' command."""
32 32 global ignore_termtitle
33 33
34 34 try:
35 35 # Cannot be on network share when issuing system commands
36 36 curr = os.getcwd()
37 37 os.chdir("C:")
38 38 ret = os.system("title " + title)
39 39 finally:
40 40 os.chdir(curr)
41 41 if ret:
42 42 # non-zero return code signals error, don't try again
43 43 ignore_termtitle = True
44 44
45
45 46 def find_cmd(cmd):
46 47 """Find the full path to a .bat or .exe using the win32api module."""
47 48 try:
48 49 import win32api
49 50 except ImportError:
50 51 raise ImportError('you need to have pywin32 installed for this to work')
51 52 else:
52 53 try:
53 54 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe')
54 55 except:
55 56 (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat')
56 57 return path
57 58
58 59
59 60 def get_long_path_name(path):
60 61 """Get a long path name (expand ~) on Windows using ctypes.
61 62
62 63 Examples
63 64 --------
64 65
65 66 >>> get_long_path_name('c:\\docume~1')
66 67 u'c:\\\\Documents and Settings'
67 68
68 69 """
69 70 try:
70 71 import ctypes
71 72 except ImportError:
72 73 raise ImportError('you need to have ctypes installed for this to work')
73 74 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
74 75 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
75 76 ctypes.c_uint ]
76 77
77 78 buf = ctypes.create_unicode_buffer(260)
78 79 rv = _GetLongPathName(path, buf, 260)
79 80 if rv == 0 or rv > 260:
80 81 return path
81 82 else:
82 83 return buf.value
84
85
86 def term_clear():
87 os.system('cls')
General Comments 0
You need to be logged in to leave comments. Login now