##// END OF EJS Templates
Fixing a few small things on Windows....
bgranger -
Show More
@@ -1,700 +1,700 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Facilities for launching processing asynchronously.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import os
19 19 import re
20 20 import sys
21 21
22 22 from IPython.core.component import Component
23 23 from IPython.external import Itpl
24 24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 25 from IPython.kernel.twistedutil import gatherBoth, make_deferred, sleep_deferred
26 26
27 27 from twisted.internet import reactor, defer
28 28 from twisted.internet.defer import inlineCallbacks
29 29 from twisted.internet.protocol import ProcessProtocol
30 30 from twisted.internet.utils import getProcessOutput
31 31 from twisted.internet.error import ProcessDone, ProcessTerminated
32 32 from twisted.python import log
33 33 from twisted.python.failure import Failure
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Generic launchers
37 37 #-----------------------------------------------------------------------------
38 38
39 39
40 40 class LauncherError(Exception):
41 41 pass
42 42
43 43
44 44 class ProcessStateError(LauncherError):
45 45 pass
46 46
47 47
48 48 class UnknownStatus(LauncherError):
49 49 pass
50 50
51 51
52 52 class BaseLauncher(Component):
53 53 """An asbtraction for starting, stopping and signaling a process."""
54 54
55 55 # A directory for files related to the process. But, we don't cd to
56 56 # this directory,
57 57 working_dir = Unicode(u'')
58 58
59 59 def __init__(self, working_dir, parent=None, name=None, config=None):
60 60 super(BaseLauncher, self).__init__(parent, name, config)
61 61 self.working_dir = working_dir
62 62 self.state = 'before' # can be before, running, after
63 63 self.stop_deferreds = []
64 64 self.start_data = None
65 65 self.stop_data = None
66 66
67 67 @property
68 68 def args(self):
69 69 """A list of cmd and args that will be used to start the process.
70 70
71 71 This is what is passed to :func:`spawnProcess` and the first element
72 72 will be the process name.
73 73 """
74 74 return self.find_args()
75 75
76 76 def find_args(self):
77 77 """The ``.args`` property calls this to find the args list.
78 78
79 79 Subcommand should implement this to construct the cmd and args.
80 80 """
81 81 raise NotImplementedError('find_args must be implemented in a subclass')
82 82
83 83 @property
84 84 def arg_str(self):
85 85 """The string form of the program arguments."""
86 86 return ' '.join(self.args)
87 87
88 88 @property
89 89 def running(self):
90 90 """Am I running."""
91 91 if self.state == 'running':
92 92 return True
93 93 else:
94 94 return False
95 95
96 96 def start(self):
97 97 """Start the process.
98 98
99 99 This must return a deferred that fires with information about the
100 100 process starting (like a pid, job id, etc.).
101 101 """
102 102 return defer.fail(
103 103 Failure(NotImplementedError(
104 104 'start must be implemented in a subclass')
105 105 )
106 106 )
107 107
108 108 def stop(self):
109 109 """Stop the process and notify observers of stopping.
110 110
111 111 This must return a deferred that fires with information about the
112 112 processing stopping, like errors that occur while the process is
113 113 attempting to be shut down. This deferred won't fire when the process
114 114 actually stops. To observe the actual process stopping, see
115 115 :func:`observe_stop`.
116 116 """
117 117 return defer.fail(
118 118 Failure(NotImplementedError(
119 119 'stop must be implemented in a subclass')
120 120 )
121 121 )
122 122
123 123 def observe_stop(self):
124 124 """Get a deferred that will fire when the process stops.
125 125
126 126 The deferred will fire with data that contains information about
127 127 the exit status of the process.
128 128 """
129 129 if self.state=='after':
130 130 return defer.succeed(self.stop_data)
131 131 else:
132 132 d = defer.Deferred()
133 133 self.stop_deferreds.append(d)
134 134 return d
135 135
136 136 def notify_start(self, data):
137 137 """Call this to trigger startup actions.
138 138
139 139 This logs the process startup and sets the state to 'running'. It is
140 140 a pass-through so it can be used as a callback.
141 141 """
142 142
143 143 log.msg('Process %r started: %r' % (self.args[0], data))
144 144 self.start_data = data
145 145 self.state = 'running'
146 146 return data
147 147
148 148 def notify_stop(self, data):
149 149 """Call this to trigger process stop actions.
150 150
151 151 This logs the process stopping and sets the state to 'after'. Call
152 152 this to trigger all the deferreds from :func:`observe_stop`."""
153 153
154 154 log.msg('Process %r stopped: %r' % (self.args[0], data))
155 155 self.stop_data = data
156 156 self.state = 'after'
157 157 for i in range(len(self.stop_deferreds)):
158 158 d = self.stop_deferreds.pop()
159 159 d.callback(data)
160 160 return data
161 161
162 162 def signal(self, sig):
163 163 """Signal the process.
164 164
165 165 Return a semi-meaningless deferred after signaling the process.
166 166
167 167 Parameters
168 168 ----------
169 169 sig : str or int
170 170 'KILL', 'INT', etc., or any signal number
171 171 """
172 172 return defer.fail(
173 173 Failure(NotImplementedError(
174 174 'signal must be implemented in a subclass')
175 175 )
176 176 )
177 177
178 178
179 179 class LocalProcessLauncherProtocol(ProcessProtocol):
180 180 """A ProcessProtocol to go with the LocalProcessLauncher."""
181 181
182 182 def __init__(self, process_launcher):
183 183 self.process_launcher = process_launcher
184 184 self.pid = None
185 185
186 186 def connectionMade(self):
187 187 self.pid = self.transport.pid
188 188 self.process_launcher.notify_start(self.transport.pid)
189 189
190 190 def processEnded(self, status):
191 191 value = status.value
192 192 if isinstance(value, ProcessDone):
193 193 self.process_launcher.notify_stop(
194 194 {'exit_code':0,
195 195 'signal':None,
196 196 'status':None,
197 197 'pid':self.pid
198 198 }
199 199 )
200 200 elif isinstance(value, ProcessTerminated):
201 201 self.process_launcher.notify_stop(
202 202 {'exit_code':value.exitCode,
203 203 'signal':value.signal,
204 204 'status':value.status,
205 205 'pid':self.pid
206 206 }
207 207 )
208 208 else:
209 209 raise UnknownStatus("Unknown exit status, this is probably a "
210 210 "bug in Twisted")
211 211
212 212 def outReceived(self, data):
213 213 log.msg(data)
214 214
215 215 def errReceived(self, data):
216 216 log.err(data)
217 217
218 218
219 219 class LocalProcessLauncher(BaseLauncher):
220 220 """Start and stop an external process in an asynchronous manner."""
221 221
222 222 # This is used to to construct self.args, which is passed to
223 223 # spawnProcess.
224 224 cmd_and_args = List([])
225 225
226 226 def __init__(self, working_dir, parent=None, name=None, config=None):
227 227 super(LocalProcessLauncher, self).__init__(
228 228 working_dir, parent, name, config
229 229 )
230 230 self.process_protocol = None
231 231 self.start_deferred = None
232 232
233 233 def find_args(self):
234 234 return self.cmd_and_args
235 235
236 236 def start(self):
237 237 if self.state == 'before':
238 238 self.process_protocol = LocalProcessLauncherProtocol(self)
239 239 self.start_deferred = defer.Deferred()
240 240 self.process_transport = reactor.spawnProcess(
241 241 self.process_protocol,
242 242 str(self.args[0]),
243 243 [str(a) for a in self.args],
244 244 env=os.environ
245 245 )
246 246 return self.start_deferred
247 247 else:
248 248 s = 'The process was already started and has state: %r' % self.state
249 249 return defer.fail(ProcessStateError(s))
250 250
251 251 def notify_start(self, data):
252 252 super(LocalProcessLauncher, self).notify_start(data)
253 253 self.start_deferred.callback(data)
254 254
255 255 def stop(self):
256 256 return self.interrupt_then_kill()
257 257
258 258 @make_deferred
259 259 def signal(self, sig):
260 260 if self.state == 'running':
261 261 self.process_transport.signalProcess(sig)
262 262
263 263 @inlineCallbacks
264 264 def interrupt_then_kill(self, delay=2.0):
265 265 """Send INT, wait a delay and then send KILL."""
266 266 yield self.signal('INT')
267 267 yield sleep_deferred(delay)
268 268 yield self.signal('KILL')
269 269
270 270
271 271 class MPIExecLauncher(LocalProcessLauncher):
272 272 """Launch an external process using mpiexec."""
273 273
274 274 # The mpiexec command to use in starting the process.
275 275 mpi_cmd = List(['mpiexec'], config=True)
276 276 # The command line arguments to pass to mpiexec.
277 277 mpi_args = List([], config=True)
278 278 # The program to start using mpiexec.
279 279 program = List(['date'], config=True)
280 280 # The command line argument to the program.
281 281 program_args = List([], config=True)
282 282 # The number of instances of the program to start.
283 283 n = Int(1, config=True)
284 284
285 285 def find_args(self):
286 286 """Build self.args using all the fields."""
287 287 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
288 288 self.program + self.program_args
289 289
290 290 def start(self, n):
291 291 """Start n instances of the program using mpiexec."""
292 292 self.n = n
293 293 return super(MPIExecLauncher, self).start()
294 294
295 295
296 296 class SSHLauncher(BaseLauncher):
297 297 """A minimal launcher for ssh.
298 298
299 299 To be useful this will probably have to be extended to use the ``sshx``
300 300 idea for environment variables. There could be other things this needs
301 301 as well.
302 302 """
303 303
304 304 ssh_cmd = List(['ssh'], config=True)
305 305 ssh_args = List([], config=True)
306 306 program = List(['date'], config=True)
307 307 program_args = List([], config=True)
308 308 hostname = Str('', config=True)
309 user = Str(os.environ['USER'], config=True)
309 user = Str('', config=True)
310 310 location = Str('')
311 311
312 312 def _hostname_changed(self, name, old, new):
313 313 self.location = '%s@%s' % (self.user, new)
314 314
315 315 def _user_changed(self, name, old, new):
316 316 self.location = '%s@%s' % (new, self.hostname)
317 317
318 318 def find_args(self):
319 319 return self.ssh_cmd + self.ssh_args + [self.location] + \
320 320 self.program + self.program_args
321 321
322 322 def start(self, n, hostname=None, user=None):
323 323 if hostname is not None:
324 324 self.hostname = hostname
325 325 if user is not None:
326 326 self.user = user
327 327 return super(SSHLauncher, self).start()
328 328
329 329
330 330 class WindowsHPCLauncher(BaseLauncher):
331 331 pass
332 332
333 333
334 334 class BatchSystemLauncher(BaseLauncher):
335 335 """Launch an external process using a batch system.
336 336
337 337 This class is designed to work with UNIX batch systems like PBS, LSF,
338 338 GridEngine, etc. The overall model is that there are different commands
339 339 like qsub, qdel, etc. that handle the starting and stopping of the process.
340 340
341 341 This class also has the notion of a batch script. The ``batch_template``
342 342 attribute can be set to a string that is a template for the batch script.
343 343 This template is instantiated using Itpl. Thus the template can use
344 344 ${n} fot the number of instances. Subclasses can add additional variables
345 345 to the template dict.
346 346 """
347 347
348 348 # Subclasses must fill these in. See PBSEngineSet
349 349 # The name of the command line program used to submit jobs.
350 350 submit_command = Str('', config=True)
351 351 # The name of the command line program used to delete jobs.
352 352 delete_command = Str('', config=True)
353 353 # A regular expression used to get the job id from the output of the
354 354 # submit_command.
355 355 job_id_regexp = Str('', config=True)
356 356 # The string that is the batch script template itself.
357 357 batch_template = Str('', config=True)
358 358 # The filename of the instantiated batch script.
359 359 batch_file_name = Unicode(u'batch_script', config=True)
360 360 # The full path to the instantiated batch script.
361 361 batch_file = Unicode(u'')
362 362
363 363 def __init__(self, working_dir, parent=None, name=None, config=None):
364 364 super(BatchSystemLauncher, self).__init__(
365 365 working_dir, parent, name, config
366 366 )
367 367 self.batch_file = os.path.join(self.working_dir, self.batch_file_name)
368 368 self.context = {}
369 369
370 370 def parse_job_id(self, output):
371 371 """Take the output of the submit command and return the job id."""
372 372 m = re.match(self.job_id_regexp, output)
373 373 if m is not None:
374 374 job_id = m.group()
375 375 else:
376 376 raise LauncherError("Job id couldn't be determined: %s" % output)
377 377 self.job_id = job_id
378 378 log.msg('Job started with job id: %r' % job_id)
379 379 return job_id
380 380
381 381 def write_batch_script(self, n):
382 382 """Instantiate and write the batch script to the working_dir."""
383 383 self.context['n'] = n
384 384 script_as_string = Itpl.itplns(self.batch_template, self.context)
385 385 log.msg('Writing instantiated batch script: %s' % self.batch_file)
386 386 f = open(self.batch_file, 'w')
387 387 f.write(script_as_string)
388 388 f.close()
389 389
390 390 @inlineCallbacks
391 391 def start(self, n):
392 392 """Start n copies of the process using a batch system."""
393 393 self.write_batch_script(n)
394 394 output = yield getProcessOutput(self.submit_command,
395 395 [self.batch_file], env=os.environ)
396 396 job_id = self.parse_job_id(output)
397 397 self.notify_start(job_id)
398 398 defer.returnValue(job_id)
399 399
400 400 @inlineCallbacks
401 401 def stop(self):
402 402 output = yield getProcessOutput(self.delete_command,
403 403 [self.job_id], env=os.environ
404 404 )
405 405 self.notify_stop(output) # Pass the output of the kill cmd
406 406 defer.returnValue(output)
407 407
408 408
409 409 class PBSLauncher(BatchSystemLauncher):
410 410 """A BatchSystemLauncher subclass for PBS."""
411 411
412 412 submit_command = Str('qsub', config=True)
413 413 delete_command = Str('qdel', config=True)
414 414 job_id_regexp = Str('\d+', config=True)
415 415 batch_template = Str('', config=True)
416 416 batch_file_name = Unicode(u'pbs_batch_script', config=True)
417 417 batch_file = Unicode(u'')
418 418
419 419
420 420 #-----------------------------------------------------------------------------
421 421 # Controller launchers
422 422 #-----------------------------------------------------------------------------
423 423
424 424 def find_controller_cmd():
425 425 """Find the command line ipcontroller program in a cross platform way."""
426 426 if sys.platform == 'win32':
427 427 # This logic is needed because the ipcontroller script doesn't
428 428 # always get installed in the same way or in the same location.
429 429 from IPython.kernel import ipcontrollerapp
430 430 script_location = ipcontrollerapp.__file__.replace('.pyc', '.py')
431 431 # The -u option here turns on unbuffered output, which is required
432 432 # on Win32 to prevent wierd conflict and problems with Twisted.
433 433 # Also, use sys.executable to make sure we are picking up the
434 434 # right python exe.
435 435 cmd = [sys.executable, '-u', script_location]
436 436 else:
437 437 # ipcontroller has to be on the PATH in this case.
438 438 cmd = ['ipcontroller']
439 439 return cmd
440 440
441 441
442 442 class LocalControllerLauncher(LocalProcessLauncher):
443 443 """Launch a controller as a regular external process."""
444 444
445 445 controller_cmd = List(find_controller_cmd())
446 446 # Command line arguments to ipcontroller.
447 447 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
448 448
449 449 def find_args(self):
450 450 return self.controller_cmd + self.controller_args
451 451
452 452 def start(self, profile=None, cluster_dir=None):
453 453 """Start the controller by profile or cluster_dir."""
454 454 if cluster_dir is not None:
455 455 self.controller_args.extend(['--cluster-dir', cluster_dir])
456 456 if profile is not None:
457 457 self.controller_args.extend(['--profile', profile])
458 458 log.msg("Starting LocalControllerLauncher: %r" % self.args)
459 459 return super(LocalControllerLauncher, self).start()
460 460
461 461
462 462 class WindowsHPCControllerLauncher(WindowsHPCLauncher):
463 463 pass
464 464
465 465
466 466 class MPIExecControllerLauncher(MPIExecLauncher):
467 467 """Launch a controller using mpiexec."""
468 468
469 469 controller_cmd = List(find_controller_cmd(), config=False)
470 470 # Command line arguments to ipcontroller.
471 471 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
472 472 n = Int(1, config=False)
473 473
474 474 def start(self, profile=None, cluster_dir=None):
475 475 """Start the controller by profile or cluster_dir."""
476 476 if cluster_dir is not None:
477 477 self.controller_args.extend(['--cluster-dir', cluster_dir])
478 478 if profile is not None:
479 479 self.controller_args.extend(['--profile', profile])
480 480 log.msg("Starting MPIExecControllerLauncher: %r" % self.args)
481 481 return super(MPIExecControllerLauncher, self).start(1)
482 482
483 483 def find_args(self):
484 484 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
485 485 self.controller_cmd + self.controller_args
486 486
487 487
488 488 class PBSControllerLauncher(PBSLauncher):
489 489 """Launch a controller using PBS."""
490 490
491 491 batch_file_name = Unicode(u'pbs_batch_script_controller', config=True)
492 492
493 493 def start(self, profile=None, cluster_dir=None):
494 494 """Start the controller by profile or cluster_dir."""
495 495 # Here we save profile and cluster_dir in the context so they
496 496 # can be used in the batch script template as ${profile} and
497 497 # ${cluster_dir}
498 498 if cluster_dir is not None:
499 499 self.context['cluster_dir'] = cluster_dir
500 500 if profile is not None:
501 501 self.context['profile'] = profile
502 502 log.msg("Starting PBSControllerLauncher: %r" % self.args)
503 503 return super(PBSControllerLauncher, self).start(1)
504 504
505 505
506 506 class SSHControllerLauncher(SSHLauncher):
507 507 pass
508 508
509 509
510 510 #-----------------------------------------------------------------------------
511 511 # Engine launchers
512 512 #-----------------------------------------------------------------------------
513 513
514 514
515 515 def find_engine_cmd():
516 516 """Find the command line ipengine program in a cross platform way."""
517 517 if sys.platform == 'win32':
518 518 # This logic is needed because the ipengine script doesn't
519 519 # always get installed in the same way or in the same location.
520 520 from IPython.kernel import ipengineapp
521 521 script_location = ipengineapp.__file__.replace('.pyc', '.py')
522 522 # The -u option here turns on unbuffered output, which is required
523 523 # on Win32 to prevent wierd conflict and problems with Twisted.
524 524 # Also, use sys.executable to make sure we are picking up the
525 525 # right python exe.
526 526 cmd = [sys.executable, '-u', script_location]
527 527 else:
528 528 # ipcontroller has to be on the PATH in this case.
529 529 cmd = ['ipengine']
530 530 return cmd
531 531
532 532
533 533 class LocalEngineLauncher(LocalProcessLauncher):
534 534 """Launch a single engine as a regular externall process."""
535 535
536 536 engine_cmd = List(find_engine_cmd())
537 537 # Command line arguments for ipengine.
538 538 engine_args = List(
539 539 ['--log-to-file','--log-level', '40'], config=True
540 540 )
541 541
542 542 def find_args(self):
543 543 return self.engine_cmd + self.engine_args
544 544
545 545 def start(self, profile=None, cluster_dir=None):
546 546 """Start the engine by profile or cluster_dir."""
547 547 if cluster_dir is not None:
548 548 self.engine_args.extend(['--cluster-dir', cluster_dir])
549 549 if profile is not None:
550 550 self.engine_args.extend(['--profile', profile])
551 551 return super(LocalEngineLauncher, self).start()
552 552
553 553
554 554 class LocalEngineSetLauncher(BaseLauncher):
555 555 """Launch a set of engines as regular external processes."""
556 556
557 557 # Command line arguments for ipengine.
558 558 engine_args = List(
559 559 ['--log-to-file','--log-level', '40'], config=True
560 560 )
561 561
562 562 def __init__(self, working_dir, parent=None, name=None, config=None):
563 563 super(LocalEngineSetLauncher, self).__init__(
564 564 working_dir, parent, name, config
565 565 )
566 566 self.launchers = []
567 567
568 568 def start(self, n, profile=None, cluster_dir=None):
569 569 """Start n engines by profile or cluster_dir."""
570 570 dlist = []
571 571 for i in range(n):
572 572 el = LocalEngineLauncher(self.working_dir, self)
573 573 # Copy the engine args over to each engine launcher.
574 574 import copy
575 575 el.engine_args = copy.deepcopy(self.engine_args)
576 576 d = el.start(profile, cluster_dir)
577 577 if i==0:
578 578 log.msg("Starting LocalEngineSetLauncher: %r" % el.args)
579 579 self.launchers.append(el)
580 580 dlist.append(d)
581 581 # The consumeErrors here could be dangerous
582 582 dfinal = gatherBoth(dlist, consumeErrors=True)
583 583 dfinal.addCallback(self.notify_start)
584 584 return dfinal
585 585
586 586 def find_args(self):
587 587 return ['engine set']
588 588
589 589 def signal(self, sig):
590 590 dlist = []
591 591 for el in self.launchers:
592 592 d = el.signal(sig)
593 593 dlist.append(d)
594 594 dfinal = gatherBoth(dlist, consumeErrors=True)
595 595 return dfinal
596 596
597 597 def interrupt_then_kill(self, delay=1.0):
598 598 dlist = []
599 599 for el in self.launchers:
600 600 d = el.interrupt_then_kill(delay)
601 601 dlist.append(d)
602 602 dfinal = gatherBoth(dlist, consumeErrors=True)
603 603 return dfinal
604 604
605 605 def stop(self):
606 606 return self.interrupt_then_kill()
607 607
608 608 def observe_stop(self):
609 609 dlist = [el.observe_stop() for el in self.launchers]
610 610 dfinal = gatherBoth(dlist, consumeErrors=False)
611 611 dfinal.addCallback(self.notify_stop)
612 612 return dfinal
613 613
614 614
615 615 class MPIExecEngineSetLauncher(MPIExecLauncher):
616 616
617 617 engine_cmd = List(find_engine_cmd(), config=False)
618 618 # Command line arguments for ipengine.
619 619 engine_args = List(
620 620 ['--log-to-file','--log-level', '40'], config=True
621 621 )
622 622 n = Int(1, config=True)
623 623
624 624 def start(self, n, profile=None, cluster_dir=None):
625 625 """Start n engines by profile or cluster_dir."""
626 626 if cluster_dir is not None:
627 627 self.engine_args.extend(['--cluster-dir', cluster_dir])
628 628 if profile is not None:
629 629 self.engine_args.extend(['--profile', profile])
630 630 log.msg('Starting MPIExecEngineSetLauncher: %r' % self.args)
631 631 return super(MPIExecEngineSetLauncher, self).start(n)
632 632
633 633 def find_args(self):
634 634 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
635 635 self.engine_cmd + self.engine_args
636 636
637 637
638 638 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher):
639 639 pass
640 640
641 641
642 642 class PBSEngineSetLauncher(PBSLauncher):
643 643
644 644 batch_file_name = Unicode(u'pbs_batch_script_engines', config=True)
645 645
646 646 def start(self, n, profile=None, cluster_dir=None):
647 647 """Start n engines by profile or cluster_dir."""
648 648 if cluster_dir is not None:
649 649 self.program_args.extend(['--cluster-dir', cluster_dir])
650 650 if profile is not None:
651 651 self.program_args.extend(['-p', profile])
652 652 log.msg('Starting PBSEngineSetLauncher: %r' % self.args)
653 653 return super(PBSEngineSetLauncher, self).start(n)
654 654
655 655
656 656 class SSHEngineSetLauncher(BaseLauncher):
657 657 pass
658 658
659 659
660 660 #-----------------------------------------------------------------------------
661 661 # A launcher for ipcluster itself!
662 662 #-----------------------------------------------------------------------------
663 663
664 664
665 665 def find_ipcluster_cmd():
666 666 """Find the command line ipcluster program in a cross platform way."""
667 667 if sys.platform == 'win32':
668 668 # This logic is needed because the ipcluster script doesn't
669 669 # always get installed in the same way or in the same location.
670 670 from IPython.kernel import ipclusterapp
671 671 script_location = ipclusterapp.__file__.replace('.pyc', '.py')
672 672 # The -u option here turns on unbuffered output, which is required
673 673 # on Win32 to prevent wierd conflict and problems with Twisted.
674 674 # Also, use sys.executable to make sure we are picking up the
675 675 # right python exe.
676 676 cmd = [sys.executable, '-u', script_location]
677 677 else:
678 678 # ipcontroller has to be on the PATH in this case.
679 679 cmd = ['ipcluster']
680 680 return cmd
681 681
682 682
683 683 class IPClusterLauncher(LocalProcessLauncher):
684 684 """Launch the ipcluster program in an external process."""
685 685
686 686 ipcluster_cmd = List(find_ipcluster_cmd())
687 687 # Command line arguments to pass to ipcluster.
688 688 ipcluster_args = List(
689 689 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
690 690 ipcluster_subcommand = Str('start')
691 691 ipcluster_n = Int(2)
692 692
693 693 def find_args(self):
694 694 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
695 695 ['-n', repr(self.ipcluster_n)] + self.ipcluster_args
696 696
697 697 def start(self):
698 698 log.msg("Starting ipcluster: %r" % self.args)
699 699 return super(IPClusterLauncher, self).start()
700 700
@@ -1,1769 +1,1796 b''
1 1 # -*- coding: utf-8 -*-
2 2 """General purpose utilities.
3 3
4 4 This is a grab-bag of stuff I find useful in most programs I write. Some of
5 5 these things are also convenient when working at the command line.
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 #****************************************************************************
16 16 # required modules from the Python standard library
17 17 import __main__
18 18
19 19 import os
20 20 import platform
21 21 import re
22 22 import shlex
23 23 import shutil
24 24 import subprocess
25 25 import sys
26 26 import time
27 27 import types
28 28 import warnings
29 29
30 30 # Curses and termios are Unix-only modules
31 31 try:
32 32 import curses
33 33 # We need termios as well, so if its import happens to raise, we bail on
34 34 # using curses altogether.
35 35 import termios
36 36 except ImportError:
37 37 USE_CURSES = False
38 38 else:
39 39 # Curses on Solaris may not be complete, so we can't use it there
40 40 USE_CURSES = hasattr(curses,'initscr')
41 41
42 42 # Other IPython utilities
43 43 import IPython
44 44 from IPython.external.Itpl import itpl,printpl
45 45 from IPython.utils import platutils
46 46 from IPython.utils.generics import result_display
47 47 from IPython.external.path import path
48 48
49 49 try:
50 50 set
51 51 except:
52 52 from sets import Set as set
53 53
54 54
55 55 #****************************************************************************
56 56 # Exceptions
57 57 class Error(Exception):
58 58 """Base class for exceptions in this module."""
59 59 pass
60 60
61 61 #----------------------------------------------------------------------------
62 62 class IOStream:
63 63 def __init__(self,stream,fallback):
64 64 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
65 65 stream = fallback
66 66 self.stream = stream
67 67 self._swrite = stream.write
68 68 self.flush = stream.flush
69 69
70 70 def write(self,data):
71 71 try:
72 72 self._swrite(data)
73 73 except:
74 74 try:
75 75 # print handles some unicode issues which may trip a plain
76 76 # write() call. Attempt to emulate write() by using a
77 77 # trailing comma
78 78 print >> self.stream, data,
79 79 except:
80 80 # if we get here, something is seriously broken.
81 81 print >> sys.stderr, \
82 82 'ERROR - failed to write data to stream:', self.stream
83 83
84 84 def close(self):
85 85 pass
86 86
87 87
88 88 class IOTerm:
89 89 """ Term holds the file or file-like objects for handling I/O operations.
90 90
91 91 These are normally just sys.stdin, sys.stdout and sys.stderr but for
92 92 Windows they can can replaced to allow editing the strings before they are
93 93 displayed."""
94 94
95 95 # In the future, having IPython channel all its I/O operations through
96 96 # this class will make it easier to embed it into other environments which
97 97 # are not a normal terminal (such as a GUI-based shell)
98 98 def __init__(self,cin=None,cout=None,cerr=None):
99 99 self.cin = IOStream(cin,sys.stdin)
100 100 self.cout = IOStream(cout,sys.stdout)
101 101 self.cerr = IOStream(cerr,sys.stderr)
102 102
103 103 # Global variable to be used for all I/O
104 104 Term = IOTerm()
105 105
106 106 import IPython.utils.rlineimpl as readline
107 107 # Remake Term to use the readline i/o facilities
108 108 if sys.platform == 'win32' and readline.have_readline:
109 109
110 110 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
111 111
112 112
113 113 #****************************************************************************
114 114 # Generic warning/error printer, used by everything else
115 115 def warn(msg,level=2,exit_val=1):
116 116 """Standard warning printer. Gives formatting consistency.
117 117
118 118 Output is sent to Term.cerr (sys.stderr by default).
119 119
120 120 Options:
121 121
122 122 -level(2): allows finer control:
123 123 0 -> Do nothing, dummy function.
124 124 1 -> Print message.
125 125 2 -> Print 'WARNING:' + message. (Default level).
126 126 3 -> Print 'ERROR:' + message.
127 127 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
128 128
129 129 -exit_val (1): exit value returned by sys.exit() for a level 4
130 130 warning. Ignored for all other levels."""
131 131
132 132 if level>0:
133 133 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
134 134 print >> Term.cerr, '%s%s' % (header[level],msg)
135 135 if level == 4:
136 136 print >> Term.cerr,'Exiting.\n'
137 137 sys.exit(exit_val)
138 138
139 139 def info(msg):
140 140 """Equivalent to warn(msg,level=1)."""
141 141
142 142 warn(msg,level=1)
143 143
144 144 def error(msg):
145 145 """Equivalent to warn(msg,level=3)."""
146 146
147 147 warn(msg,level=3)
148 148
149 149 def fatal(msg,exit_val=1):
150 150 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
151 151
152 152 warn(msg,exit_val=exit_val,level=4)
153 153
154 154 #---------------------------------------------------------------------------
155 155 # Debugging routines
156 156 #
157 157 def debugx(expr,pre_msg=''):
158 158 """Print the value of an expression from the caller's frame.
159 159
160 160 Takes an expression, evaluates it in the caller's frame and prints both
161 161 the given expression and the resulting value (as well as a debug mark
162 162 indicating the name of the calling function. The input must be of a form
163 163 suitable for eval().
164 164
165 165 An optional message can be passed, which will be prepended to the printed
166 166 expr->value pair."""
167 167
168 168 cf = sys._getframe(1)
169 169 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
170 170 eval(expr,cf.f_globals,cf.f_locals))
171 171
172 172 # deactivate it by uncommenting the following line, which makes it a no-op
173 173 #def debugx(expr,pre_msg=''): pass
174 174
175 175 #----------------------------------------------------------------------------
176 176 StringTypes = types.StringTypes
177 177
178 178 # Basic timing functionality
179 179
180 180 # If possible (Unix), use the resource module instead of time.clock()
181 181 try:
182 182 import resource
183 183 def clocku():
184 184 """clocku() -> floating point number
185 185
186 186 Return the *USER* CPU time in seconds since the start of the process.
187 187 This is done via a call to resource.getrusage, so it avoids the
188 188 wraparound problems in time.clock()."""
189 189
190 190 return resource.getrusage(resource.RUSAGE_SELF)[0]
191 191
192 192 def clocks():
193 193 """clocks() -> floating point number
194 194
195 195 Return the *SYSTEM* CPU time in seconds since the start of the process.
196 196 This is done via a call to resource.getrusage, so it avoids the
197 197 wraparound problems in time.clock()."""
198 198
199 199 return resource.getrusage(resource.RUSAGE_SELF)[1]
200 200
201 201 def clock():
202 202 """clock() -> floating point number
203 203
204 204 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
205 205 the process. This is done via a call to resource.getrusage, so it
206 206 avoids the wraparound problems in time.clock()."""
207 207
208 208 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
209 209 return u+s
210 210
211 211 def clock2():
212 212 """clock2() -> (t_user,t_system)
213 213
214 214 Similar to clock(), but return a tuple of user/system times."""
215 215 return resource.getrusage(resource.RUSAGE_SELF)[:2]
216 216
217 217 except ImportError:
218 218 # There is no distinction of user/system time under windows, so we just use
219 219 # time.clock() for everything...
220 220 clocku = clocks = clock = time.clock
221 221 def clock2():
222 222 """Under windows, system CPU time can't be measured.
223 223
224 224 This just returns clock() and zero."""
225 225 return time.clock(),0.0
226 226
227 227 def timings_out(reps,func,*args,**kw):
228 228 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
229 229
230 230 Execute a function reps times, return a tuple with the elapsed total
231 231 CPU time in seconds, the time per call and the function's output.
232 232
233 233 Under Unix, the return value is the sum of user+system time consumed by
234 234 the process, computed via the resource module. This prevents problems
235 235 related to the wraparound effect which the time.clock() function has.
236 236
237 237 Under Windows the return value is in wall clock seconds. See the
238 238 documentation for the time module for more details."""
239 239
240 240 reps = int(reps)
241 241 assert reps >=1, 'reps must be >= 1'
242 242 if reps==1:
243 243 start = clock()
244 244 out = func(*args,**kw)
245 245 tot_time = clock()-start
246 246 else:
247 247 rng = xrange(reps-1) # the last time is executed separately to store output
248 248 start = clock()
249 249 for dummy in rng: func(*args,**kw)
250 250 out = func(*args,**kw) # one last time
251 251 tot_time = clock()-start
252 252 av_time = tot_time / reps
253 253 return tot_time,av_time,out
254 254
255 255 def timings(reps,func,*args,**kw):
256 256 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
257 257
258 258 Execute a function reps times, return a tuple with the elapsed total CPU
259 259 time in seconds and the time per call. These are just the first two values
260 260 in timings_out()."""
261 261
262 262 return timings_out(reps,func,*args,**kw)[0:2]
263 263
264 264 def timing(func,*args,**kw):
265 265 """timing(func,*args,**kw) -> t_total
266 266
267 267 Execute a function once, return the elapsed total CPU time in
268 268 seconds. This is just the first value in timings_out()."""
269 269
270 270 return timings_out(1,func,*args,**kw)[0]
271 271
272 272 #****************************************************************************
273 273 # file and system
274 274
275 275 def arg_split(s,posix=False):
276 276 """Split a command line's arguments in a shell-like manner.
277 277
278 278 This is a modified version of the standard library's shlex.split()
279 279 function, but with a default of posix=False for splitting, so that quotes
280 280 in inputs are respected."""
281 281
282 282 # XXX - there may be unicode-related problems here!!! I'm not sure that
283 283 # shlex is truly unicode-safe, so it might be necessary to do
284 284 #
285 285 # s = s.encode(sys.stdin.encoding)
286 286 #
287 287 # first, to ensure that shlex gets a normal string. Input from anyone who
288 288 # knows more about unicode and shlex than I would be good to have here...
289 289 lex = shlex.shlex(s, posix=posix)
290 290 lex.whitespace_split = True
291 291 return list(lex)
292 292
293 293 def system(cmd,verbose=0,debug=0,header=''):
294 294 """Execute a system command, return its exit status.
295 295
296 296 Options:
297 297
298 298 - verbose (0): print the command to be executed.
299 299
300 300 - debug (0): only print, do not actually execute.
301 301
302 302 - header (''): Header to print on screen prior to the executed command (it
303 303 is only prepended to the command, no newlines are added).
304 304
305 305 Note: a stateful version of this function is available through the
306 306 SystemExec class."""
307 307
308 308 stat = 0
309 309 if verbose or debug: print header+cmd
310 310 sys.stdout.flush()
311 311 if not debug: stat = os.system(cmd)
312 312 return stat
313 313
314 314 def abbrev_cwd():
315 315 """ Return abbreviated version of cwd, e.g. d:mydir """
316 316 cwd = os.getcwd().replace('\\','/')
317 317 drivepart = ''
318 318 tail = cwd
319 319 if sys.platform == 'win32':
320 320 if len(cwd) < 4:
321 321 return cwd
322 322 drivepart,tail = os.path.splitdrive(cwd)
323 323
324 324
325 325 parts = tail.split('/')
326 326 if len(parts) > 2:
327 327 tail = '/'.join(parts[-2:])
328 328
329 329 return (drivepart + (
330 330 cwd == '/' and '/' or tail))
331 331
332 332
333 333 # This function is used by ipython in a lot of places to make system calls.
334 334 # We need it to be slightly different under win32, due to the vagaries of
335 335 # 'network shares'. A win32 override is below.
336 336
337 337 def shell(cmd,verbose=0,debug=0,header=''):
338 338 """Execute a command in the system shell, always return None.
339 339
340 340 Options:
341 341
342 342 - verbose (0): print the command to be executed.
343 343
344 344 - debug (0): only print, do not actually execute.
345 345
346 346 - header (''): Header to print on screen prior to the executed command (it
347 347 is only prepended to the command, no newlines are added).
348 348
349 349 Note: this is similar to genutils.system(), but it returns None so it can
350 350 be conveniently used in interactive loops without getting the return value
351 351 (typically 0) printed many times."""
352 352
353 353 stat = 0
354 354 if verbose or debug: print header+cmd
355 355 # flush stdout so we don't mangle python's buffering
356 356 sys.stdout.flush()
357 357
358 358 if not debug:
359 359 platutils.set_term_title("IPy " + cmd)
360 360 os.system(cmd)
361 361 platutils.set_term_title("IPy " + abbrev_cwd())
362 362
363 363 # override shell() for win32 to deal with network shares
364 364 if os.name in ('nt','dos'):
365 365
366 366 shell_ori = shell
367 367
368 368 def shell(cmd,verbose=0,debug=0,header=''):
369 369 if os.getcwd().startswith(r"\\"):
370 370 path = os.getcwd()
371 371 # change to c drive (cannot be on UNC-share when issuing os.system,
372 372 # as cmd.exe cannot handle UNC addresses)
373 373 os.chdir("c:")
374 374 # issue pushd to the UNC-share and then run the command
375 375 try:
376 376 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
377 377 finally:
378 378 os.chdir(path)
379 379 else:
380 380 shell_ori(cmd,verbose,debug,header)
381 381
382 382 shell.__doc__ = shell_ori.__doc__
383 383
384 384 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
385 385 """Dummy substitute for perl's backquotes.
386 386
387 387 Executes a command and returns the output.
388 388
389 389 Accepts the same arguments as system(), plus:
390 390
391 391 - split(0): if true, the output is returned as a list split on newlines.
392 392
393 393 Note: a stateful version of this function is available through the
394 394 SystemExec class.
395 395
396 396 This is pretty much deprecated and rarely used,
397 397 genutils.getoutputerror may be what you need.
398 398
399 399 """
400 400
401 401 if verbose or debug: print header+cmd
402 402 if not debug:
403 403 output = os.popen(cmd).read()
404 404 # stipping last \n is here for backwards compat.
405 405 if output.endswith('\n'):
406 406 output = output[:-1]
407 407 if split:
408 408 return output.split('\n')
409 409 else:
410 410 return output
411 411
412 412 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
413 413 """Return (standard output,standard error) of executing cmd in a shell.
414 414
415 415 Accepts the same arguments as system(), plus:
416 416
417 417 - split(0): if true, each of stdout/err is returned as a list split on
418 418 newlines.
419 419
420 420 Note: a stateful version of this function is available through the
421 421 SystemExec class."""
422 422
423 423 if verbose or debug: print header+cmd
424 424 if not cmd:
425 425 if split:
426 426 return [],[]
427 427 else:
428 428 return '',''
429 429 if not debug:
430 430 pin,pout,perr = os.popen3(cmd)
431 431 tout = pout.read().rstrip()
432 432 terr = perr.read().rstrip()
433 433 pin.close()
434 434 pout.close()
435 435 perr.close()
436 436 if split:
437 437 return tout.split('\n'),terr.split('\n')
438 438 else:
439 439 return tout,terr
440 440
441 441 # for compatibility with older naming conventions
442 442 xsys = system
443 443 bq = getoutput
444 444
445 445 class SystemExec:
446 446 """Access the system and getoutput functions through a stateful interface.
447 447
448 448 Note: here we refer to the system and getoutput functions from this
449 449 library, not the ones from the standard python library.
450 450
451 451 This class offers the system and getoutput functions as methods, but the
452 452 verbose, debug and header parameters can be set for the instance (at
453 453 creation time or later) so that they don't need to be specified on each
454 454 call.
455 455
456 456 For efficiency reasons, there's no way to override the parameters on a
457 457 per-call basis other than by setting instance attributes. If you need
458 458 local overrides, it's best to directly call system() or getoutput().
459 459
460 460 The following names are provided as alternate options:
461 461 - xsys: alias to system
462 462 - bq: alias to getoutput
463 463
464 464 An instance can then be created as:
465 465 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
466 466 """
467 467
468 468 def __init__(self,verbose=0,debug=0,header='',split=0):
469 469 """Specify the instance's values for verbose, debug and header."""
470 470 setattr_list(self,'verbose debug header split')
471 471
472 472 def system(self,cmd):
473 473 """Stateful interface to system(), with the same keyword parameters."""
474 474
475 475 system(cmd,self.verbose,self.debug,self.header)
476 476
477 477 def shell(self,cmd):
478 478 """Stateful interface to shell(), with the same keyword parameters."""
479 479
480 480 shell(cmd,self.verbose,self.debug,self.header)
481 481
482 482 xsys = system # alias
483 483
484 484 def getoutput(self,cmd):
485 485 """Stateful interface to getoutput()."""
486 486
487 487 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
488 488
489 489 def getoutputerror(self,cmd):
490 490 """Stateful interface to getoutputerror()."""
491 491
492 492 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
493 493
494 494 bq = getoutput # alias
495 495
496 496 #-----------------------------------------------------------------------------
497 497 def mutex_opts(dict,ex_op):
498 498 """Check for presence of mutually exclusive keys in a dict.
499 499
500 500 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
501 501 for op1,op2 in ex_op:
502 502 if op1 in dict and op2 in dict:
503 503 raise ValueError,'\n*** ERROR in Arguments *** '\
504 504 'Options '+op1+' and '+op2+' are mutually exclusive.'
505 505
506 506 #-----------------------------------------------------------------------------
507 507 def get_py_filename(name):
508 508 """Return a valid python filename in the current directory.
509 509
510 510 If the given name is not a file, it adds '.py' and searches again.
511 511 Raises IOError with an informative message if the file isn't found."""
512 512
513 513 name = os.path.expanduser(name)
514 514 if not os.path.isfile(name) and not name.endswith('.py'):
515 515 name += '.py'
516 516 if os.path.isfile(name):
517 517 return name
518 518 else:
519 519 raise IOError,'File `%s` not found.' % name
520 520
521 521 #-----------------------------------------------------------------------------
522 522
523 523
524 524 def filefind(filename, path_dirs=None):
525 525 """Find a file by looking through a sequence of paths.
526 526
527 527 This iterates through a sequence of paths looking for a file and returns
528 528 the full, absolute path of the first occurence of the file. If no set of
529 529 path dirs is given, the filename is tested as is, after running through
530 530 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
531 531
532 532 filefind('myfile.txt')
533 533
534 534 will find the file in the current working dir, but::
535 535
536 536 filefind('~/myfile.txt')
537 537
538 538 Will find the file in the users home directory. This function does not
539 539 automatically try any paths, such as the cwd or the user's home directory.
540 540
541 541 Parameters
542 542 ----------
543 543 filename : str
544 544 The filename to look for.
545 545 path_dirs : str, None or sequence of str
546 546 The sequence of paths to look for the file in. If None, the filename
547 547 need to be absolute or be in the cwd. If a string, the string is
548 548 put into a sequence and the searched. If a sequence, walk through
549 549 each element and join with ``filename``, calling :func:`expandvars`
550 550 and :func:`expanduser` before testing for existence.
551 551
552 552 Returns
553 553 -------
554 554 Raises :exc:`IOError` or returns absolute path to file.
555 555 """
556 556 if path_dirs is None:
557 557 path_dirs = ("",)
558 558 elif isinstance(path_dirs, basestring):
559 559 path_dirs = (path_dirs,)
560 560 for path in path_dirs:
561 561 if path == '.': path = os.getcwd()
562 562 testname = os.path.expandvars(
563 563 os.path.expanduser(
564 564 os.path.join(path, filename)))
565 565 if os.path.isfile(testname):
566 566 return os.path.abspath(testname)
567 567 raise IOError("File does not exist in any "
568 568 "of the search paths: %r, %r" % \
569 569 (filename, path_dirs))
570 570
571 571
572 572 #----------------------------------------------------------------------------
573 573 def file_read(filename):
574 574 """Read a file and close it. Returns the file source."""
575 575 fobj = open(filename,'r');
576 576 source = fobj.read();
577 577 fobj.close()
578 578 return source
579 579
580 580 def file_readlines(filename):
581 581 """Read a file and close it. Returns the file source using readlines()."""
582 582 fobj = open(filename,'r');
583 583 lines = fobj.readlines();
584 584 fobj.close()
585 585 return lines
586 586
587 587 #----------------------------------------------------------------------------
588 588 def target_outdated(target,deps):
589 589 """Determine whether a target is out of date.
590 590
591 591 target_outdated(target,deps) -> 1/0
592 592
593 593 deps: list of filenames which MUST exist.
594 594 target: single filename which may or may not exist.
595 595
596 596 If target doesn't exist or is older than any file listed in deps, return
597 597 true, otherwise return false.
598 598 """
599 599 try:
600 600 target_time = os.path.getmtime(target)
601 601 except os.error:
602 602 return 1
603 603 for dep in deps:
604 604 dep_time = os.path.getmtime(dep)
605 605 if dep_time > target_time:
606 606 #print "For target",target,"Dep failed:",dep # dbg
607 607 #print "times (dep,tar):",dep_time,target_time # dbg
608 608 return 1
609 609 return 0
610 610
611 611 #-----------------------------------------------------------------------------
612 612 def target_update(target,deps,cmd):
613 613 """Update a target with a given command given a list of dependencies.
614 614
615 615 target_update(target,deps,cmd) -> runs cmd if target is outdated.
616 616
617 617 This is just a wrapper around target_outdated() which calls the given
618 618 command if target is outdated."""
619 619
620 620 if target_outdated(target,deps):
621 621 xsys(cmd)
622 622
623 623 #----------------------------------------------------------------------------
624 624 def unquote_ends(istr):
625 625 """Remove a single pair of quotes from the endpoints of a string."""
626 626
627 627 if not istr:
628 628 return istr
629 629 if (istr[0]=="'" and istr[-1]=="'") or \
630 630 (istr[0]=='"' and istr[-1]=='"'):
631 631 return istr[1:-1]
632 632 else:
633 633 return istr
634 634
635 635 #----------------------------------------------------------------------------
636 636 def flag_calls(func):
637 637 """Wrap a function to detect and flag when it gets called.
638 638
639 639 This is a decorator which takes a function and wraps it in a function with
640 640 a 'called' attribute. wrapper.called is initialized to False.
641 641
642 642 The wrapper.called attribute is set to False right before each call to the
643 643 wrapped function, so if the call fails it remains False. After the call
644 644 completes, wrapper.called is set to True and the output is returned.
645 645
646 646 Testing for truth in wrapper.called allows you to determine if a call to
647 647 func() was attempted and succeeded."""
648 648
649 649 def wrapper(*args,**kw):
650 650 wrapper.called = False
651 651 out = func(*args,**kw)
652 652 wrapper.called = True
653 653 return out
654 654
655 655 wrapper.called = False
656 656 wrapper.__doc__ = func.__doc__
657 657 return wrapper
658 658
659 659 #----------------------------------------------------------------------------
660 660 def dhook_wrap(func,*a,**k):
661 661 """Wrap a function call in a sys.displayhook controller.
662 662
663 663 Returns a wrapper around func which calls func, with all its arguments and
664 664 keywords unmodified, using the default sys.displayhook. Since IPython
665 665 modifies sys.displayhook, it breaks the behavior of certain systems that
666 666 rely on the default behavior, notably doctest.
667 667 """
668 668
669 669 def f(*a,**k):
670 670
671 671 dhook_s = sys.displayhook
672 672 sys.displayhook = sys.__displayhook__
673 673 try:
674 674 out = func(*a,**k)
675 675 finally:
676 676 sys.displayhook = dhook_s
677 677
678 678 return out
679 679
680 680 f.__doc__ = func.__doc__
681 681 return f
682 682
683 683 #----------------------------------------------------------------------------
684 684 def doctest_reload():
685 685 """Properly reload doctest to reuse it interactively.
686 686
687 687 This routine:
688 688
689 689 - imports doctest but does NOT reload it (see below).
690 690
691 691 - resets its global 'master' attribute to None, so that multiple uses of
692 692 the module interactively don't produce cumulative reports.
693 693
694 694 - Monkeypatches its core test runner method to protect it from IPython's
695 695 modified displayhook. Doctest expects the default displayhook behavior
696 696 deep down, so our modification breaks it completely. For this reason, a
697 697 hard monkeypatch seems like a reasonable solution rather than asking
698 698 users to manually use a different doctest runner when under IPython.
699 699
700 700 Notes
701 701 -----
702 702
703 703 This function *used to* reload doctest, but this has been disabled because
704 704 reloading doctest unconditionally can cause massive breakage of other
705 705 doctest-dependent modules already in memory, such as those for IPython's
706 706 own testing system. The name wasn't changed to avoid breaking people's
707 707 code, but the reload call isn't actually made anymore."""
708 708
709 709 import doctest
710 710 doctest.master = None
711 711 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
712 712
713 713 #----------------------------------------------------------------------------
714 714 class HomeDirError(Error):
715 715 pass
716 716
717 717 def get_home_dir():
718 718 """Return the closest possible equivalent to a 'home' directory.
719 719
720 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
721
720 * On POSIX, we try $HOME.
721 * On Windows we try:
722 - %HOMESHARE%
723 - %HOMEDRIVE\%HOMEPATH%
724 - %USERPROFILE%
725 - Registry hack
726 * On Dos C:\
727
722 728 Currently only Posix and NT are implemented, a HomeDirError exception is
723 raised for all other OSes. """
729 raised for all other OSes.
730 """
724 731
725 732 isdir = os.path.isdir
726 733 env = os.environ
727 734
728 735 # first, check py2exe distribution root directory for _ipython.
729 736 # This overrides all. Normally does not exist.
730 737
731 738 if hasattr(sys, "frozen"): #Is frozen by py2exe
732 739 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
733 740 root, rest = IPython.__file__.lower().split('library.zip')
734 741 else:
735 742 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
736 743 root=os.path.abspath(root).rstrip('\\')
737 744 if isdir(os.path.join(root, '_ipython')):
738 745 os.environ["IPYKITROOT"] = root
739 746 return root.decode(sys.getfilesystemencoding())
740 try:
741 homedir = env['HOME']
742 if not isdir(homedir):
743 # in case a user stuck some string which does NOT resolve to a
744 # valid path, it's as good as if we hadn't foud it
745 raise KeyError
746 return homedir.decode(sys.getfilesystemencoding())
747 except KeyError:
748 if os.name == 'posix':
749 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
750 elif os.name == 'nt':
751 # For some strange reason, win9x returns 'nt' for os.name.
752 try:
753 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
754 if not isdir(homedir):
755 homedir = os.path.join(env['USERPROFILE'])
756 if not isdir(homedir):
757 raise HomeDirError
747
748 if os.name == 'posix':
749 # Linux, Unix, AIX, OS X
750 try:
751 homedir = env['HOME']
752 except KeyError:
753 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
754 else:
755 return homedir.decode(sys.getfilesystemencoding())
756 elif os.name == 'nt':
757 # Now for win9x, XP, Vista, 7?
758 # For some strange reason all of these return 'nt' for os.name.
759 # First look for a network home directory. This will return the UNC
760 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
761 # is needed when running IPython on cluster where all paths have to
762 # be UNC.
763 try:
764 homedir = env['HOMESHARE']
765 except KeyError:
766 pass
767 else:
768 if isdir(homedir):
758 769 return homedir.decode(sys.getfilesystemencoding())
759 except KeyError:
760 try:
761 # Use the registry to get the 'My Documents' folder.
762 import _winreg as wreg
763 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
764 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
765 homedir = wreg.QueryValueEx(key,'Personal')[0]
766 key.Close()
767 if not isdir(homedir):
768 e = ('Invalid "Personal" folder registry key '
769 'typically "My Documents".\n'
770 'Value: %s\n'
771 'This is not a valid directory on your system.' %
772 homedir)
773 raise HomeDirError(e)
774 return homedir.decode(sys.getfilesystemencoding())
775 except HomeDirError:
776 raise
777 except:
778 return 'C:\\'.decode(sys.getfilesystemencoding())
779 elif os.name == 'dos':
780 # Desperate, may do absurd things in classic MacOS. May work under DOS.
781 return 'C:\\'.decode(sys.getfilesystemencoding())
770
771 # Now look for a local home directory
772 try:
773 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
774 except KeyError:
775 pass
782 776 else:
783 raise HomeDirError,'support for your operating system not implemented.'
777 if isdir(homedir):
778 return homedir.decode(sys.getfilesystemencoding())
779
780 # Now the users profile directory
781 try:
782 homedir = os.path.join(env['USERPROFILE'])
783 except KeyError:
784 pass
785 else:
786 if isdir(homedir):
787 return homedir.decode(sys.getfilesystemencoding())
788
789 # Use the registry to get the 'My Documents' folder.
790 try:
791 import _winreg as wreg
792 key = wreg.OpenKey(
793 wreg.HKEY_CURRENT_USER,
794 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
795 )
796 homedir = wreg.QueryValueEx(key,'Personal')[0]
797 key.Close()
798 except:
799 pass
800 else:
801 if isdir(homedir):
802 return homedir.decode(sys.getfilesystemencoding())
803
804 # If all else fails, raise HomeDirError
805 raise HomeDirError('No valid home directory could be found')
806 elif os.name == 'dos':
807 # Desperate, may do absurd things in classic MacOS. May work under DOS.
808 return 'C:\\'.decode(sys.getfilesystemencoding())
809 else:
810 raise HomeDirError('No valid home directory could be found for your OS')
784 811
785 812
786 813 def get_ipython_dir():
787 814 """Get the IPython directory for this platform and user.
788 815
789 816 This uses the logic in `get_home_dir` to find the home directory
790 817 and the adds .ipython to the end of the path.
791 818 """
792 819 ipdir_def = '.ipython'
793 820 home_dir = get_home_dir()
794 821 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
795 822 os.path.join(home_dir, ipdir_def)))
796 823 return ipdir.decode(sys.getfilesystemencoding())
797 824
798 825
799 826 #****************************************************************************
800 827 # strings and text
801 828
802 829 class LSString(str):
803 830 """String derivative with a special access attributes.
804 831
805 832 These are normal strings, but with the special attributes:
806 833
807 834 .l (or .list) : value as list (split on newlines).
808 835 .n (or .nlstr): original value (the string itself).
809 836 .s (or .spstr): value as whitespace-separated string.
810 837 .p (or .paths): list of path objects
811 838
812 839 Any values which require transformations are computed only once and
813 840 cached.
814 841
815 842 Such strings are very useful to efficiently interact with the shell, which
816 843 typically only understands whitespace-separated options for commands."""
817 844
818 845 def get_list(self):
819 846 try:
820 847 return self.__list
821 848 except AttributeError:
822 849 self.__list = self.split('\n')
823 850 return self.__list
824 851
825 852 l = list = property(get_list)
826 853
827 854 def get_spstr(self):
828 855 try:
829 856 return self.__spstr
830 857 except AttributeError:
831 858 self.__spstr = self.replace('\n',' ')
832 859 return self.__spstr
833 860
834 861 s = spstr = property(get_spstr)
835 862
836 863 def get_nlstr(self):
837 864 return self
838 865
839 866 n = nlstr = property(get_nlstr)
840 867
841 868 def get_paths(self):
842 869 try:
843 870 return self.__paths
844 871 except AttributeError:
845 872 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
846 873 return self.__paths
847 874
848 875 p = paths = property(get_paths)
849 876
850 877 def print_lsstring(arg):
851 878 """ Prettier (non-repr-like) and more informative printer for LSString """
852 879 print "LSString (.p, .n, .l, .s available). Value:"
853 880 print arg
854 881
855 882 print_lsstring = result_display.when_type(LSString)(print_lsstring)
856 883
857 884 #----------------------------------------------------------------------------
858 885 class SList(list):
859 886 """List derivative with a special access attributes.
860 887
861 888 These are normal lists, but with the special attributes:
862 889
863 890 .l (or .list) : value as list (the list itself).
864 891 .n (or .nlstr): value as a string, joined on newlines.
865 892 .s (or .spstr): value as a string, joined on spaces.
866 893 .p (or .paths): list of path objects
867 894
868 895 Any values which require transformations are computed only once and
869 896 cached."""
870 897
871 898 def get_list(self):
872 899 return self
873 900
874 901 l = list = property(get_list)
875 902
876 903 def get_spstr(self):
877 904 try:
878 905 return self.__spstr
879 906 except AttributeError:
880 907 self.__spstr = ' '.join(self)
881 908 return self.__spstr
882 909
883 910 s = spstr = property(get_spstr)
884 911
885 912 def get_nlstr(self):
886 913 try:
887 914 return self.__nlstr
888 915 except AttributeError:
889 916 self.__nlstr = '\n'.join(self)
890 917 return self.__nlstr
891 918
892 919 n = nlstr = property(get_nlstr)
893 920
894 921 def get_paths(self):
895 922 try:
896 923 return self.__paths
897 924 except AttributeError:
898 925 self.__paths = [path(p) for p in self if os.path.exists(p)]
899 926 return self.__paths
900 927
901 928 p = paths = property(get_paths)
902 929
903 930 def grep(self, pattern, prune = False, field = None):
904 931 """ Return all strings matching 'pattern' (a regex or callable)
905 932
906 933 This is case-insensitive. If prune is true, return all items
907 934 NOT matching the pattern.
908 935
909 936 If field is specified, the match must occur in the specified
910 937 whitespace-separated field.
911 938
912 939 Examples::
913 940
914 941 a.grep( lambda x: x.startswith('C') )
915 942 a.grep('Cha.*log', prune=1)
916 943 a.grep('chm', field=-1)
917 944 """
918 945
919 946 def match_target(s):
920 947 if field is None:
921 948 return s
922 949 parts = s.split()
923 950 try:
924 951 tgt = parts[field]
925 952 return tgt
926 953 except IndexError:
927 954 return ""
928 955
929 956 if isinstance(pattern, basestring):
930 957 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
931 958 else:
932 959 pred = pattern
933 960 if not prune:
934 961 return SList([el for el in self if pred(match_target(el))])
935 962 else:
936 963 return SList([el for el in self if not pred(match_target(el))])
937 964 def fields(self, *fields):
938 965 """ Collect whitespace-separated fields from string list
939 966
940 967 Allows quick awk-like usage of string lists.
941 968
942 969 Example data (in var a, created by 'a = !ls -l')::
943 970 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
944 971 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
945 972
946 973 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
947 974 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
948 975 (note the joining by space).
949 976 a.fields(-1) is ['ChangeLog', 'IPython']
950 977
951 978 IndexErrors are ignored.
952 979
953 980 Without args, fields() just split()'s the strings.
954 981 """
955 982 if len(fields) == 0:
956 983 return [el.split() for el in self]
957 984
958 985 res = SList()
959 986 for el in [f.split() for f in self]:
960 987 lineparts = []
961 988
962 989 for fd in fields:
963 990 try:
964 991 lineparts.append(el[fd])
965 992 except IndexError:
966 993 pass
967 994 if lineparts:
968 995 res.append(" ".join(lineparts))
969 996
970 997 return res
971 998 def sort(self,field= None, nums = False):
972 999 """ sort by specified fields (see fields())
973 1000
974 1001 Example::
975 1002 a.sort(1, nums = True)
976 1003
977 1004 Sorts a by second field, in numerical order (so that 21 > 3)
978 1005
979 1006 """
980 1007
981 1008 #decorate, sort, undecorate
982 1009 if field is not None:
983 1010 dsu = [[SList([line]).fields(field), line] for line in self]
984 1011 else:
985 1012 dsu = [[line, line] for line in self]
986 1013 if nums:
987 1014 for i in range(len(dsu)):
988 1015 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
989 1016 try:
990 1017 n = int(numstr)
991 1018 except ValueError:
992 1019 n = 0;
993 1020 dsu[i][0] = n
994 1021
995 1022
996 1023 dsu.sort()
997 1024 return SList([t[1] for t in dsu])
998 1025
999 1026 def print_slist(arg):
1000 1027 """ Prettier (non-repr-like) and more informative printer for SList """
1001 1028 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1002 1029 if hasattr(arg, 'hideonce') and arg.hideonce:
1003 1030 arg.hideonce = False
1004 1031 return
1005 1032
1006 1033 nlprint(arg)
1007 1034
1008 1035 print_slist = result_display.when_type(SList)(print_slist)
1009 1036
1010 1037
1011 1038
1012 1039 #----------------------------------------------------------------------------
1013 1040 def esc_quotes(strng):
1014 1041 """Return the input string with single and double quotes escaped out"""
1015 1042
1016 1043 return strng.replace('"','\\"').replace("'","\\'")
1017 1044
1018 1045 #----------------------------------------------------------------------------
1019 1046 def make_quoted_expr(s):
1020 1047 """Return string s in appropriate quotes, using raw string if possible.
1021 1048
1022 1049 XXX - example removed because it caused encoding errors in documentation
1023 1050 generation. We need a new example that doesn't contain invalid chars.
1024 1051
1025 1052 Note the use of raw string and padding at the end to allow trailing
1026 1053 backslash.
1027 1054 """
1028 1055
1029 1056 tail = ''
1030 1057 tailpadding = ''
1031 1058 raw = ''
1032 1059 if "\\" in s:
1033 1060 raw = 'r'
1034 1061 if s.endswith('\\'):
1035 1062 tail = '[:-1]'
1036 1063 tailpadding = '_'
1037 1064 if '"' not in s:
1038 1065 quote = '"'
1039 1066 elif "'" not in s:
1040 1067 quote = "'"
1041 1068 elif '"""' not in s and not s.endswith('"'):
1042 1069 quote = '"""'
1043 1070 elif "'''" not in s and not s.endswith("'"):
1044 1071 quote = "'''"
1045 1072 else:
1046 1073 # give up, backslash-escaped string will do
1047 1074 return '"%s"' % esc_quotes(s)
1048 1075 res = raw + quote + s + tailpadding + quote + tail
1049 1076 return res
1050 1077
1051 1078
1052 1079 #----------------------------------------------------------------------------
1053 1080 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1054 1081 """Take multiple lines of input.
1055 1082
1056 1083 A list with each line of input as a separate element is returned when a
1057 1084 termination string is entered (defaults to a single '.'). Input can also
1058 1085 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1059 1086
1060 1087 Lines of input which end in \\ are joined into single entries (and a
1061 1088 secondary continuation prompt is issued as long as the user terminates
1062 1089 lines with \\). This allows entering very long strings which are still
1063 1090 meant to be treated as single entities.
1064 1091 """
1065 1092
1066 1093 try:
1067 1094 if header:
1068 1095 header += '\n'
1069 1096 lines = [raw_input(header + ps1)]
1070 1097 except EOFError:
1071 1098 return []
1072 1099 terminate = [terminate_str]
1073 1100 try:
1074 1101 while lines[-1:] != terminate:
1075 1102 new_line = raw_input(ps1)
1076 1103 while new_line.endswith('\\'):
1077 1104 new_line = new_line[:-1] + raw_input(ps2)
1078 1105 lines.append(new_line)
1079 1106
1080 1107 return lines[:-1] # don't return the termination command
1081 1108 except EOFError:
1082 1109 print
1083 1110 return lines
1084 1111
1085 1112 #----------------------------------------------------------------------------
1086 1113 def raw_input_ext(prompt='', ps2='... '):
1087 1114 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1088 1115
1089 1116 line = raw_input(prompt)
1090 1117 while line.endswith('\\'):
1091 1118 line = line[:-1] + raw_input(ps2)
1092 1119 return line
1093 1120
1094 1121 #----------------------------------------------------------------------------
1095 1122 def ask_yes_no(prompt,default=None):
1096 1123 """Asks a question and returns a boolean (y/n) answer.
1097 1124
1098 1125 If default is given (one of 'y','n'), it is used if the user input is
1099 1126 empty. Otherwise the question is repeated until an answer is given.
1100 1127
1101 1128 An EOF is treated as the default answer. If there is no default, an
1102 1129 exception is raised to prevent infinite loops.
1103 1130
1104 1131 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1105 1132
1106 1133 answers = {'y':True,'n':False,'yes':True,'no':False}
1107 1134 ans = None
1108 1135 while ans not in answers.keys():
1109 1136 try:
1110 1137 ans = raw_input(prompt+' ').lower()
1111 1138 if not ans: # response was an empty string
1112 1139 ans = default
1113 1140 except KeyboardInterrupt:
1114 1141 pass
1115 1142 except EOFError:
1116 1143 if default in answers.keys():
1117 1144 ans = default
1118 1145 print
1119 1146 else:
1120 1147 raise
1121 1148
1122 1149 return answers[ans]
1123 1150
1124 1151 #----------------------------------------------------------------------------
1125 1152 class EvalDict:
1126 1153 """
1127 1154 Emulate a dict which evaluates its contents in the caller's frame.
1128 1155
1129 1156 Usage:
1130 1157 >>> number = 19
1131 1158
1132 1159 >>> text = "python"
1133 1160
1134 1161 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1135 1162 Python 2.1 rules!
1136 1163 """
1137 1164
1138 1165 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1139 1166 # modified (shorter) version of:
1140 1167 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1141 1168 # Skip Montanaro (skip@pobox.com).
1142 1169
1143 1170 def __getitem__(self, name):
1144 1171 frame = sys._getframe(1)
1145 1172 return eval(name, frame.f_globals, frame.f_locals)
1146 1173
1147 1174 EvalString = EvalDict # for backwards compatibility
1148 1175 #----------------------------------------------------------------------------
1149 1176 def qw(words,flat=0,sep=None,maxsplit=-1):
1150 1177 """Similar to Perl's qw() operator, but with some more options.
1151 1178
1152 1179 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1153 1180
1154 1181 words can also be a list itself, and with flat=1, the output will be
1155 1182 recursively flattened.
1156 1183
1157 1184 Examples:
1158 1185
1159 1186 >>> qw('1 2')
1160 1187 ['1', '2']
1161 1188
1162 1189 >>> qw(['a b','1 2',['m n','p q']])
1163 1190 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1164 1191
1165 1192 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1166 1193 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1167 1194 """
1168 1195
1169 1196 if type(words) in StringTypes:
1170 1197 return [word.strip() for word in words.split(sep,maxsplit)
1171 1198 if word and not word.isspace() ]
1172 1199 if flat:
1173 1200 return flatten(map(qw,words,[1]*len(words)))
1174 1201 return map(qw,words)
1175 1202
1176 1203 #----------------------------------------------------------------------------
1177 1204 def qwflat(words,sep=None,maxsplit=-1):
1178 1205 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1179 1206 return qw(words,1,sep,maxsplit)
1180 1207
1181 1208 #----------------------------------------------------------------------------
1182 1209 def qw_lol(indata):
1183 1210 """qw_lol('a b') -> [['a','b']],
1184 1211 otherwise it's just a call to qw().
1185 1212
1186 1213 We need this to make sure the modules_some keys *always* end up as a
1187 1214 list of lists."""
1188 1215
1189 1216 if type(indata) in StringTypes:
1190 1217 return [qw(indata)]
1191 1218 else:
1192 1219 return qw(indata)
1193 1220
1194 1221 #----------------------------------------------------------------------------
1195 1222 def grep(pat,list,case=1):
1196 1223 """Simple minded grep-like function.
1197 1224 grep(pat,list) returns occurrences of pat in list, None on failure.
1198 1225
1199 1226 It only does simple string matching, with no support for regexps. Use the
1200 1227 option case=0 for case-insensitive matching."""
1201 1228
1202 1229 # This is pretty crude. At least it should implement copying only references
1203 1230 # to the original data in case it's big. Now it copies the data for output.
1204 1231 out=[]
1205 1232 if case:
1206 1233 for term in list:
1207 1234 if term.find(pat)>-1: out.append(term)
1208 1235 else:
1209 1236 lpat=pat.lower()
1210 1237 for term in list:
1211 1238 if term.lower().find(lpat)>-1: out.append(term)
1212 1239
1213 1240 if len(out): return out
1214 1241 else: return None
1215 1242
1216 1243 #----------------------------------------------------------------------------
1217 1244 def dgrep(pat,*opts):
1218 1245 """Return grep() on dir()+dir(__builtins__).
1219 1246
1220 1247 A very common use of grep() when working interactively."""
1221 1248
1222 1249 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1223 1250
1224 1251 #----------------------------------------------------------------------------
1225 1252 def idgrep(pat):
1226 1253 """Case-insensitive dgrep()"""
1227 1254
1228 1255 return dgrep(pat,0)
1229 1256
1230 1257 #----------------------------------------------------------------------------
1231 1258 def igrep(pat,list):
1232 1259 """Synonym for case-insensitive grep."""
1233 1260
1234 1261 return grep(pat,list,case=0)
1235 1262
1236 1263 #----------------------------------------------------------------------------
1237 1264 def indent(str,nspaces=4,ntabs=0):
1238 1265 """Indent a string a given number of spaces or tabstops.
1239 1266
1240 1267 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1241 1268 """
1242 1269 if str is None:
1243 1270 return
1244 1271 ind = '\t'*ntabs+' '*nspaces
1245 1272 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1246 1273 if outstr.endswith(os.linesep+ind):
1247 1274 return outstr[:-len(ind)]
1248 1275 else:
1249 1276 return outstr
1250 1277
1251 1278 #-----------------------------------------------------------------------------
1252 1279 def native_line_ends(filename,backup=1):
1253 1280 """Convert (in-place) a file to line-ends native to the current OS.
1254 1281
1255 1282 If the optional backup argument is given as false, no backup of the
1256 1283 original file is left. """
1257 1284
1258 1285 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1259 1286
1260 1287 bak_filename = filename + backup_suffixes[os.name]
1261 1288
1262 1289 original = open(filename).read()
1263 1290 shutil.copy2(filename,bak_filename)
1264 1291 try:
1265 1292 new = open(filename,'wb')
1266 1293 new.write(os.linesep.join(original.splitlines()))
1267 1294 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1268 1295 new.close()
1269 1296 except:
1270 1297 os.rename(bak_filename,filename)
1271 1298 if not backup:
1272 1299 try:
1273 1300 os.remove(bak_filename)
1274 1301 except:
1275 1302 pass
1276 1303
1277 1304 #****************************************************************************
1278 1305 # lists, dicts and structures
1279 1306
1280 1307 def belong(candidates,checklist):
1281 1308 """Check whether a list of items appear in a given list of options.
1282 1309
1283 1310 Returns a list of 1 and 0, one for each candidate given."""
1284 1311
1285 1312 return [x in checklist for x in candidates]
1286 1313
1287 1314 #----------------------------------------------------------------------------
1288 1315 def uniq_stable(elems):
1289 1316 """uniq_stable(elems) -> list
1290 1317
1291 1318 Return from an iterable, a list of all the unique elements in the input,
1292 1319 but maintaining the order in which they first appear.
1293 1320
1294 1321 A naive solution to this problem which just makes a dictionary with the
1295 1322 elements as keys fails to respect the stability condition, since
1296 1323 dictionaries are unsorted by nature.
1297 1324
1298 1325 Note: All elements in the input must be valid dictionary keys for this
1299 1326 routine to work, as it internally uses a dictionary for efficiency
1300 1327 reasons."""
1301 1328
1302 1329 unique = []
1303 1330 unique_dict = {}
1304 1331 for nn in elems:
1305 1332 if nn not in unique_dict:
1306 1333 unique.append(nn)
1307 1334 unique_dict[nn] = None
1308 1335 return unique
1309 1336
1310 1337 #----------------------------------------------------------------------------
1311 1338 class NLprinter:
1312 1339 """Print an arbitrarily nested list, indicating index numbers.
1313 1340
1314 1341 An instance of this class called nlprint is available and callable as a
1315 1342 function.
1316 1343
1317 1344 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1318 1345 and using 'sep' to separate the index from the value. """
1319 1346
1320 1347 def __init__(self):
1321 1348 self.depth = 0
1322 1349
1323 1350 def __call__(self,lst,pos='',**kw):
1324 1351 """Prints the nested list numbering levels."""
1325 1352 kw.setdefault('indent',' ')
1326 1353 kw.setdefault('sep',': ')
1327 1354 kw.setdefault('start',0)
1328 1355 kw.setdefault('stop',len(lst))
1329 1356 # we need to remove start and stop from kw so they don't propagate
1330 1357 # into a recursive call for a nested list.
1331 1358 start = kw['start']; del kw['start']
1332 1359 stop = kw['stop']; del kw['stop']
1333 1360 if self.depth == 0 and 'header' in kw.keys():
1334 1361 print kw['header']
1335 1362
1336 1363 for idx in range(start,stop):
1337 1364 elem = lst[idx]
1338 1365 if type(elem)==type([]):
1339 1366 self.depth += 1
1340 1367 self.__call__(elem,itpl('$pos$idx,'),**kw)
1341 1368 self.depth -= 1
1342 1369 else:
1343 1370 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1344 1371
1345 1372 nlprint = NLprinter()
1346 1373 #----------------------------------------------------------------------------
1347 1374 def all_belong(candidates,checklist):
1348 1375 """Check whether a list of items ALL appear in a given list of options.
1349 1376
1350 1377 Returns a single 1 or 0 value."""
1351 1378
1352 1379 return 1-(0 in [x in checklist for x in candidates])
1353 1380
1354 1381 #----------------------------------------------------------------------------
1355 1382 def sort_compare(lst1,lst2,inplace = 1):
1356 1383 """Sort and compare two lists.
1357 1384
1358 1385 By default it does it in place, thus modifying the lists. Use inplace = 0
1359 1386 to avoid that (at the cost of temporary copy creation)."""
1360 1387 if not inplace:
1361 1388 lst1 = lst1[:]
1362 1389 lst2 = lst2[:]
1363 1390 lst1.sort(); lst2.sort()
1364 1391 return lst1 == lst2
1365 1392
1366 1393 #----------------------------------------------------------------------------
1367 1394 def list2dict(lst):
1368 1395 """Takes a list of (key,value) pairs and turns it into a dict."""
1369 1396
1370 1397 dic = {}
1371 1398 for k,v in lst: dic[k] = v
1372 1399 return dic
1373 1400
1374 1401 #----------------------------------------------------------------------------
1375 1402 def list2dict2(lst,default=''):
1376 1403 """Takes a list and turns it into a dict.
1377 1404 Much slower than list2dict, but more versatile. This version can take
1378 1405 lists with sublists of arbitrary length (including sclars)."""
1379 1406
1380 1407 dic = {}
1381 1408 for elem in lst:
1382 1409 if type(elem) in (types.ListType,types.TupleType):
1383 1410 size = len(elem)
1384 1411 if size == 0:
1385 1412 pass
1386 1413 elif size == 1:
1387 1414 dic[elem] = default
1388 1415 else:
1389 1416 k,v = elem[0], elem[1:]
1390 1417 if len(v) == 1: v = v[0]
1391 1418 dic[k] = v
1392 1419 else:
1393 1420 dic[elem] = default
1394 1421 return dic
1395 1422
1396 1423 #----------------------------------------------------------------------------
1397 1424 def flatten(seq):
1398 1425 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1399 1426
1400 1427 return [x for subseq in seq for x in subseq]
1401 1428
1402 1429 #----------------------------------------------------------------------------
1403 1430 def get_slice(seq,start=0,stop=None,step=1):
1404 1431 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1405 1432 if stop == None:
1406 1433 stop = len(seq)
1407 1434 item = lambda i: seq[i]
1408 1435 return map(item,xrange(start,stop,step))
1409 1436
1410 1437 #----------------------------------------------------------------------------
1411 1438 def chop(seq,size):
1412 1439 """Chop a sequence into chunks of the given size."""
1413 1440 chunk = lambda i: seq[i:i+size]
1414 1441 return map(chunk,xrange(0,len(seq),size))
1415 1442
1416 1443 #----------------------------------------------------------------------------
1417 1444 # with is a keyword as of python 2.5, so this function is renamed to withobj
1418 1445 # from its old 'with' name.
1419 1446 def with_obj(object, **args):
1420 1447 """Set multiple attributes for an object, similar to Pascal's with.
1421 1448
1422 1449 Example:
1423 1450 with_obj(jim,
1424 1451 born = 1960,
1425 1452 haircolour = 'Brown',
1426 1453 eyecolour = 'Green')
1427 1454
1428 1455 Credit: Greg Ewing, in
1429 1456 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1430 1457
1431 1458 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1432 1459 has become a keyword for Python 2.5, so we had to rename it."""
1433 1460
1434 1461 object.__dict__.update(args)
1435 1462
1436 1463 #----------------------------------------------------------------------------
1437 1464 def setattr_list(obj,alist,nspace = None):
1438 1465 """Set a list of attributes for an object taken from a namespace.
1439 1466
1440 1467 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1441 1468 alist with their values taken from nspace, which must be a dict (something
1442 1469 like locals() will often do) If nspace isn't given, locals() of the
1443 1470 *caller* is used, so in most cases you can omit it.
1444 1471
1445 1472 Note that alist can be given as a string, which will be automatically
1446 1473 split into a list on whitespace. If given as a list, it must be a list of
1447 1474 *strings* (the variable names themselves), not of variables."""
1448 1475
1449 1476 # this grabs the local variables from the *previous* call frame -- that is
1450 1477 # the locals from the function that called setattr_list().
1451 1478 # - snipped from weave.inline()
1452 1479 if nspace is None:
1453 1480 call_frame = sys._getframe().f_back
1454 1481 nspace = call_frame.f_locals
1455 1482
1456 1483 if type(alist) in StringTypes:
1457 1484 alist = alist.split()
1458 1485 for attr in alist:
1459 1486 val = eval(attr,nspace)
1460 1487 setattr(obj,attr,val)
1461 1488
1462 1489 #----------------------------------------------------------------------------
1463 1490 def getattr_list(obj,alist,*args):
1464 1491 """getattr_list(obj,alist[, default]) -> attribute list.
1465 1492
1466 1493 Get a list of named attributes for an object. When a default argument is
1467 1494 given, it is returned when the attribute doesn't exist; without it, an
1468 1495 exception is raised in that case.
1469 1496
1470 1497 Note that alist can be given as a string, which will be automatically
1471 1498 split into a list on whitespace. If given as a list, it must be a list of
1472 1499 *strings* (the variable names themselves), not of variables."""
1473 1500
1474 1501 if type(alist) in StringTypes:
1475 1502 alist = alist.split()
1476 1503 if args:
1477 1504 if len(args)==1:
1478 1505 default = args[0]
1479 1506 return map(lambda attr: getattr(obj,attr,default),alist)
1480 1507 else:
1481 1508 raise ValueError,'getattr_list() takes only one optional argument'
1482 1509 else:
1483 1510 return map(lambda attr: getattr(obj,attr),alist)
1484 1511
1485 1512 #----------------------------------------------------------------------------
1486 1513 def map_method(method,object_list,*argseq,**kw):
1487 1514 """map_method(method,object_list,*args,**kw) -> list
1488 1515
1489 1516 Return a list of the results of applying the methods to the items of the
1490 1517 argument sequence(s). If more than one sequence is given, the method is
1491 1518 called with an argument list consisting of the corresponding item of each
1492 1519 sequence. All sequences must be of the same length.
1493 1520
1494 1521 Keyword arguments are passed verbatim to all objects called.
1495 1522
1496 1523 This is Python code, so it's not nearly as fast as the builtin map()."""
1497 1524
1498 1525 out_list = []
1499 1526 idx = 0
1500 1527 for object in object_list:
1501 1528 try:
1502 1529 handler = getattr(object, method)
1503 1530 except AttributeError:
1504 1531 out_list.append(None)
1505 1532 else:
1506 1533 if argseq:
1507 1534 args = map(lambda lst:lst[idx],argseq)
1508 1535 #print 'ob',object,'hand',handler,'ar',args # dbg
1509 1536 out_list.append(handler(args,**kw))
1510 1537 else:
1511 1538 out_list.append(handler(**kw))
1512 1539 idx += 1
1513 1540 return out_list
1514 1541
1515 1542 #----------------------------------------------------------------------------
1516 1543 def get_class_members(cls):
1517 1544 ret = dir(cls)
1518 1545 if hasattr(cls,'__bases__'):
1519 1546 for base in cls.__bases__:
1520 1547 ret.extend(get_class_members(base))
1521 1548 return ret
1522 1549
1523 1550 #----------------------------------------------------------------------------
1524 1551 def dir2(obj):
1525 1552 """dir2(obj) -> list of strings
1526 1553
1527 1554 Extended version of the Python builtin dir(), which does a few extra
1528 1555 checks, and supports common objects with unusual internals that confuse
1529 1556 dir(), such as Traits and PyCrust.
1530 1557
1531 1558 This version is guaranteed to return only a list of true strings, whereas
1532 1559 dir() returns anything that objects inject into themselves, even if they
1533 1560 are later not really valid for attribute access (many extension libraries
1534 1561 have such bugs).
1535 1562 """
1536 1563
1537 1564 # Start building the attribute list via dir(), and then complete it
1538 1565 # with a few extra special-purpose calls.
1539 1566 words = dir(obj)
1540 1567
1541 1568 if hasattr(obj,'__class__'):
1542 1569 words.append('__class__')
1543 1570 words.extend(get_class_members(obj.__class__))
1544 1571 #if '__base__' in words: 1/0
1545 1572
1546 1573 # Some libraries (such as traits) may introduce duplicates, we want to
1547 1574 # track and clean this up if it happens
1548 1575 may_have_dupes = False
1549 1576
1550 1577 # this is the 'dir' function for objects with Enthought's traits
1551 1578 if hasattr(obj, 'trait_names'):
1552 1579 try:
1553 1580 words.extend(obj.trait_names())
1554 1581 may_have_dupes = True
1555 1582 except TypeError:
1556 1583 # This will happen if `obj` is a class and not an instance.
1557 1584 pass
1558 1585
1559 1586 # Support for PyCrust-style _getAttributeNames magic method.
1560 1587 if hasattr(obj, '_getAttributeNames'):
1561 1588 try:
1562 1589 words.extend(obj._getAttributeNames())
1563 1590 may_have_dupes = True
1564 1591 except TypeError:
1565 1592 # `obj` is a class and not an instance. Ignore
1566 1593 # this error.
1567 1594 pass
1568 1595
1569 1596 if may_have_dupes:
1570 1597 # eliminate possible duplicates, as some traits may also
1571 1598 # appear as normal attributes in the dir() call.
1572 1599 words = list(set(words))
1573 1600 words.sort()
1574 1601
1575 1602 # filter out non-string attributes which may be stuffed by dir() calls
1576 1603 # and poor coding in third-party modules
1577 1604 return [w for w in words if isinstance(w, basestring)]
1578 1605
1579 1606 #----------------------------------------------------------------------------
1580 1607 def import_fail_info(mod_name,fns=None):
1581 1608 """Inform load failure for a module."""
1582 1609
1583 1610 if fns == None:
1584 1611 warn("Loading of %s failed.\n" % (mod_name,))
1585 1612 else:
1586 1613 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1587 1614
1588 1615 #----------------------------------------------------------------------------
1589 1616 # Proposed popitem() extension, written as a method
1590 1617
1591 1618
1592 1619 class NotGiven: pass
1593 1620
1594 1621 def popkey(dct,key,default=NotGiven):
1595 1622 """Return dct[key] and delete dct[key].
1596 1623
1597 1624 If default is given, return it if dct[key] doesn't exist, otherwise raise
1598 1625 KeyError. """
1599 1626
1600 1627 try:
1601 1628 val = dct[key]
1602 1629 except KeyError:
1603 1630 if default is NotGiven:
1604 1631 raise
1605 1632 else:
1606 1633 return default
1607 1634 else:
1608 1635 del dct[key]
1609 1636 return val
1610 1637
1611 1638 def wrap_deprecated(func, suggest = '<nothing>'):
1612 1639 def newFunc(*args, **kwargs):
1613 1640 warnings.warn("Call to deprecated function %s, use %s instead" %
1614 1641 ( func.__name__, suggest),
1615 1642 category=DeprecationWarning,
1616 1643 stacklevel = 2)
1617 1644 return func(*args, **kwargs)
1618 1645 return newFunc
1619 1646
1620 1647
1621 1648 def _num_cpus_unix():
1622 1649 """Return the number of active CPUs on a Unix system."""
1623 1650 return os.sysconf("SC_NPROCESSORS_ONLN")
1624 1651
1625 1652
1626 1653 def _num_cpus_darwin():
1627 1654 """Return the number of active CPUs on a Darwin system."""
1628 1655 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
1629 1656 return p.stdout.read()
1630 1657
1631 1658
1632 1659 def _num_cpus_windows():
1633 1660 """Return the number of active CPUs on a Windows system."""
1634 1661 return os.environ.get("NUMBER_OF_PROCESSORS")
1635 1662
1636 1663
1637 1664 def num_cpus():
1638 1665 """Return the effective number of CPUs in the system as an integer.
1639 1666
1640 1667 This cross-platform function makes an attempt at finding the total number of
1641 1668 available CPUs in the system, as returned by various underlying system and
1642 1669 python calls.
1643 1670
1644 1671 If it can't find a sensible answer, it returns 1 (though an error *may* make
1645 1672 it return a large positive number that's actually incorrect).
1646 1673 """
1647 1674
1648 1675 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
1649 1676 # for the names of the keys we needed to look up for this function. This
1650 1677 # code was inspired by their equivalent function.
1651 1678
1652 1679 ncpufuncs = {'Linux':_num_cpus_unix,
1653 1680 'Darwin':_num_cpus_darwin,
1654 1681 'Windows':_num_cpus_windows,
1655 1682 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
1656 1683 # See http://bugs.python.org/issue1082 for details.
1657 1684 'Microsoft':_num_cpus_windows,
1658 1685 }
1659 1686
1660 1687 ncpufunc = ncpufuncs.get(platform.system(),
1661 1688 # default to unix version (Solaris, AIX, etc)
1662 1689 _num_cpus_unix)
1663 1690
1664 1691 try:
1665 1692 ncpus = max(1,int(ncpufunc()))
1666 1693 except:
1667 1694 ncpus = 1
1668 1695 return ncpus
1669 1696
1670 1697 def extract_vars(*names,**kw):
1671 1698 """Extract a set of variables by name from another frame.
1672 1699
1673 1700 :Parameters:
1674 1701 - `*names`: strings
1675 1702 One or more variable names which will be extracted from the caller's
1676 1703 frame.
1677 1704
1678 1705 :Keywords:
1679 1706 - `depth`: integer (0)
1680 1707 How many frames in the stack to walk when looking for your variables.
1681 1708
1682 1709
1683 1710 Examples:
1684 1711
1685 1712 In [2]: def func(x):
1686 1713 ...: y = 1
1687 1714 ...: print extract_vars('x','y')
1688 1715 ...:
1689 1716
1690 1717 In [3]: func('hello')
1691 1718 {'y': 1, 'x': 'hello'}
1692 1719 """
1693 1720
1694 1721 depth = kw.get('depth',0)
1695 1722
1696 1723 callerNS = sys._getframe(depth+1).f_locals
1697 1724 return dict((k,callerNS[k]) for k in names)
1698 1725
1699 1726
1700 1727 def extract_vars_above(*names):
1701 1728 """Extract a set of variables by name from another frame.
1702 1729
1703 1730 Similar to extractVars(), but with a specified depth of 1, so that names
1704 1731 are exctracted exactly from above the caller.
1705 1732
1706 1733 This is simply a convenience function so that the very common case (for us)
1707 1734 of skipping exactly 1 frame doesn't have to construct a special dict for
1708 1735 keyword passing."""
1709 1736
1710 1737 callerNS = sys._getframe(2).f_locals
1711 1738 return dict((k,callerNS[k]) for k in names)
1712 1739
1713 1740 def shexp(s):
1714 1741 """Expand $VARS and ~names in a string, like a shell
1715 1742
1716 1743 :Examples:
1717 1744
1718 1745 In [2]: os.environ['FOO']='test'
1719 1746
1720 1747 In [3]: shexp('variable FOO is $FOO')
1721 1748 Out[3]: 'variable FOO is test'
1722 1749 """
1723 1750 return os.path.expandvars(os.path.expanduser(s))
1724 1751
1725 1752
1726 1753 def list_strings(arg):
1727 1754 """Always return a list of strings, given a string or list of strings
1728 1755 as input.
1729 1756
1730 1757 :Examples:
1731 1758
1732 1759 In [7]: list_strings('A single string')
1733 1760 Out[7]: ['A single string']
1734 1761
1735 1762 In [8]: list_strings(['A single string in a list'])
1736 1763 Out[8]: ['A single string in a list']
1737 1764
1738 1765 In [9]: list_strings(['A','list','of','strings'])
1739 1766 Out[9]: ['A', 'list', 'of', 'strings']
1740 1767 """
1741 1768
1742 1769 if isinstance(arg,basestring): return [arg]
1743 1770 else: return arg
1744 1771
1745 1772
1746 1773 #----------------------------------------------------------------------------
1747 1774 def marquee(txt='',width=78,mark='*'):
1748 1775 """Return the input string centered in a 'marquee'.
1749 1776
1750 1777 :Examples:
1751 1778
1752 1779 In [16]: marquee('A test',40)
1753 1780 Out[16]: '**************** A test ****************'
1754 1781
1755 1782 In [17]: marquee('A test',40,'-')
1756 1783 Out[17]: '---------------- A test ----------------'
1757 1784
1758 1785 In [18]: marquee('A test',40,' ')
1759 1786 Out[18]: ' A test '
1760 1787
1761 1788 """
1762 1789 if not txt:
1763 1790 return (mark*width)[:width]
1764 1791 nmark = (width-len(txt)-2)/len(mark)/2
1765 1792 if nmark < 0: nmark =0
1766 1793 marks = mark*nmark
1767 1794 return '%s %s %s' % (marks,txt,marks)
1768 1795
1769 1796 #*************************** end of file <genutils.py> **********************
@@ -1,320 +1,320 b''
1 1 # encoding: utf-8
2 2
3 3 """
4 4 This module defines the things that are used in setup.py for building IPython
5 5
6 6 This includes:
7 7
8 8 * The basic arguments to setup
9 9 * Functions for finding things like packages, package data, etc.
10 10 * A function for checking dependencies.
11 11 """
12 12
13 13 __docformat__ = "restructuredtext en"
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Copyright (C) 2008 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-------------------------------------------------------------------------------
21 21
22 22 #-------------------------------------------------------------------------------
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 26 import os, sys
27 27
28 28 from glob import glob
29 29
30 30 from setupext import install_data_ext
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Useful globals and utility functions
34 34 #-------------------------------------------------------------------------------
35 35
36 36 # A few handy globals
37 37 isfile = os.path.isfile
38 38 pjoin = os.path.join
39 39
40 40 def oscmd(s):
41 41 print ">", s
42 42 os.system(s)
43 43
44 44 # A little utility we'll need below, since glob() does NOT allow you to do
45 45 # exclusion on multiple endings!
46 46 def file_doesnt_endwith(test,endings):
47 47 """Return true if test is a file and its name does NOT end with any
48 48 of the strings listed in endings."""
49 49 if not isfile(test):
50 50 return False
51 51 for e in endings:
52 52 if test.endswith(e):
53 53 return False
54 54 return True
55 55
56 56 #---------------------------------------------------------------------------
57 57 # Basic project information
58 58 #---------------------------------------------------------------------------
59 59
60 60 # release.py contains version, authors, license, url, keywords, etc.
61 61 execfile(pjoin('IPython','core','release.py'))
62 62
63 63 # Create a dict with the basic information
64 64 # This dict is eventually passed to setup after additional keys are added.
65 65 setup_args = dict(
66 66 name = name,
67 67 version = version,
68 68 description = description,
69 69 long_description = long_description,
70 70 author = author,
71 71 author_email = author_email,
72 72 url = url,
73 73 download_url = download_url,
74 74 license = license,
75 75 platforms = platforms,
76 76 keywords = keywords,
77 77 cmdclass = {'install_data': install_data_ext},
78 78 )
79 79
80 80
81 81 #---------------------------------------------------------------------------
82 82 # Find packages
83 83 #---------------------------------------------------------------------------
84 84
85 85 def add_package(packages,pname,config=False,tests=False,scripts=False,
86 86 others=None):
87 87 """
88 88 Add a package to the list of packages, including certain subpackages.
89 89 """
90 90 packages.append('.'.join(['IPython',pname]))
91 91 if config:
92 92 packages.append('.'.join(['IPython',pname,'config']))
93 93 if tests:
94 94 packages.append('.'.join(['IPython',pname,'tests']))
95 95 if scripts:
96 96 packages.append('.'.join(['IPython',pname,'scripts']))
97 97 if others is not None:
98 98 for o in others:
99 99 packages.append('.'.join(['IPython',pname,o]))
100 100
101 101 def find_packages():
102 102 """
103 103 Find all of IPython's packages.
104 104 """
105 105 packages = ['IPython']
106 106 add_package(packages, 'config', tests=True, others=['default','profile'])
107 107 add_package(packages, 'core', tests=True)
108 108 add_package(packages, 'deathrow', tests=True)
109 109 add_package(packages , 'extensions')
110 110 add_package(packages, 'external')
111 111 add_package(packages, 'frontend', tests=True)
112 112 # Don't include the cocoa frontend for now as it is not stable
113 113 if sys.platform == 'darwin' and False:
114 114 add_package(packages, 'frontend.cocoa', tests=True, others=['plugin'])
115 115 add_package(packages, 'frontend.cocoa.examples')
116 116 add_package(packages, 'frontend.cocoa.examples.IPython1Sandbox')
117 117 add_package(packages, 'frontend.cocoa.examples.IPython1Sandbox.English.lproj')
118 118 add_package(packages, 'frontend.process')
119 119 add_package(packages, 'frontend.wx')
120 120 add_package(packages, 'gui')
121 121 add_package(packages, 'gui.wx')
122 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
123 add_package(packages, 'kernel.core', config=True, tests=True)
122 add_package(packages, 'kernel', config=False, tests=True, scripts=True)
123 add_package(packages, 'kernel.core', config=False, tests=True)
124 124 add_package(packages, 'lib', tests=True)
125 125 add_package(packages, 'quarantine', tests=True)
126 126 add_package(packages, 'scripts')
127 127 add_package(packages, 'testing', tests=True)
128 128 add_package(packages, 'testing.plugin', tests=False)
129 129 add_package(packages, 'utils', tests=True)
130 130 return packages
131 131
132 132 #---------------------------------------------------------------------------
133 133 # Find package data
134 134 #---------------------------------------------------------------------------
135 135
136 136 def find_package_data():
137 137 """
138 138 Find IPython's package_data.
139 139 """
140 140 # This is not enough for these things to appear in an sdist.
141 141 # We need to muck with the MANIFEST to get this to work
142 142 package_data = {
143 143 'IPython.config.userconfig' : ['*'],
144 144 'IPython.testing' : ['*.txt']
145 145 }
146 146 return package_data
147 147
148 148
149 149 #---------------------------------------------------------------------------
150 150 # Find data files
151 151 #---------------------------------------------------------------------------
152 152
153 153 def make_dir_struct(tag,base,out_base):
154 154 """Make the directory structure of all files below a starting dir.
155 155
156 156 This is just a convenience routine to help build a nested directory
157 157 hierarchy because distutils is too stupid to do this by itself.
158 158
159 159 XXX - this needs a proper docstring!
160 160 """
161 161
162 162 # we'll use these a lot below
163 163 lbase = len(base)
164 164 pathsep = os.path.sep
165 165 lpathsep = len(pathsep)
166 166
167 167 out = []
168 168 for (dirpath,dirnames,filenames) in os.walk(base):
169 169 # we need to strip out the dirpath from the base to map it to the
170 170 # output (installation) path. This requires possibly stripping the
171 171 # path separator, because otherwise pjoin will not work correctly
172 172 # (pjoin('foo/','/bar') returns '/bar').
173 173
174 174 dp_eff = dirpath[lbase:]
175 175 if dp_eff.startswith(pathsep):
176 176 dp_eff = dp_eff[lpathsep:]
177 177 # The output path must be anchored at the out_base marker
178 178 out_path = pjoin(out_base,dp_eff)
179 179 # Now we can generate the final filenames. Since os.walk only produces
180 180 # filenames, we must join back with the dirpath to get full valid file
181 181 # paths:
182 182 pfiles = [pjoin(dirpath,f) for f in filenames]
183 183 # Finally, generate the entry we need, which is a triple of (tag,output
184 184 # path, files) for use as a data_files parameter in install_data.
185 185 out.append((tag,out_path,pfiles))
186 186
187 187 return out
188 188
189 189
190 190 def find_data_files():
191 191 """
192 192 Find IPython's data_files.
193 193
194 194 Most of these are docs.
195 195 """
196 196
197 197 docdirbase = pjoin('share', 'doc', 'ipython')
198 198 manpagebase = pjoin('share', 'man', 'man1')
199 199
200 200 # Simple file lists can be made by hand
201 201 manpages = filter(isfile, glob(pjoin('docs','man','*.1.gz')))
202 202 igridhelpfiles = filter(isfile, glob(pjoin('IPython','extensions','igrid_help.*')))
203 203
204 204 # For nested structures, use the utility above
205 205 example_files = make_dir_struct(
206 206 'data',
207 207 pjoin('docs','examples'),
208 208 pjoin(docdirbase,'examples')
209 209 )
210 210 manual_files = make_dir_struct(
211 211 'data',
212 212 pjoin('docs','dist'),
213 213 pjoin(docdirbase,'manual')
214 214 )
215 215
216 216 # And assemble the entire output list
217 217 data_files = [ ('data',manpagebase, manpages),
218 218 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
219 219 ] + manual_files + example_files
220 220
221 221 ## import pprint # dbg
222 222 ## print '*'*80
223 223 ## print 'data files'
224 224 ## pprint.pprint(data_files)
225 225 ## print '*'*80
226 226
227 227 return data_files
228 228
229 229
230 230 def make_man_update_target(manpage):
231 231 """Return a target_update-compliant tuple for the given manpage.
232 232
233 233 Parameters
234 234 ----------
235 235 manpage : string
236 236 Name of the manpage, must include the section number (trailing number).
237 237
238 238 Example
239 239 -------
240 240
241 241 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
242 242 ('docs/man/ipython.1.gz',
243 243 ['docs/man/ipython.1'],
244 244 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
245 245 """
246 246 man_dir = pjoin('docs', 'man')
247 247 manpage_gz = manpage + '.gz'
248 248 manpath = pjoin(man_dir, manpage)
249 249 manpath_gz = pjoin(man_dir, manpage_gz)
250 250 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
251 251 locals() )
252 252 return (manpath_gz, [manpath], gz_cmd)
253 253
254 254 #---------------------------------------------------------------------------
255 255 # Find scripts
256 256 #---------------------------------------------------------------------------
257 257
258 258 def find_scripts():
259 259 """
260 260 Find IPython's scripts.
261 261 """
262 262 kernel_scripts = pjoin('IPython','kernel','scripts')
263 263 main_scripts = pjoin('IPython','scripts')
264 264 scripts = [pjoin(kernel_scripts, 'ipengine'),
265 265 pjoin(kernel_scripts, 'ipcontroller'),
266 266 pjoin(kernel_scripts, 'ipcluster'),
267 267 pjoin(main_scripts, 'ipython'),
268 268 pjoin(main_scripts, 'ipythonx'),
269 269 pjoin(main_scripts, 'ipython-wx'),
270 270 pjoin(main_scripts, 'pycolor'),
271 271 pjoin(main_scripts, 'irunner'),
272 272 pjoin(main_scripts, 'iptest')
273 273 ]
274 274
275 275 # Script to be run by the windows binary installer after the default setup
276 276 # routine, to add shortcuts and similar windows-only things. Windows
277 277 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
278 278 # doesn't find them.
279 279 if 'bdist_wininst' in sys.argv:
280 280 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
281 281 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
282 282 sys.exit(1)
283 283 scripts.append(pjoin('scripts','ipython_win_post_install.py'))
284 284
285 285 return scripts
286 286
287 287 #---------------------------------------------------------------------------
288 288 # Verify all dependencies
289 289 #---------------------------------------------------------------------------
290 290
291 291 def check_for_dependencies():
292 292 """Check for IPython's dependencies.
293 293
294 294 This function should NOT be called if running under setuptools!
295 295 """
296 296 from setupext.setupext import (
297 297 print_line, print_raw, print_status, print_message,
298 298 check_for_zopeinterface, check_for_twisted,
299 299 check_for_foolscap, check_for_pyopenssl,
300 300 check_for_sphinx, check_for_pygments,
301 301 check_for_nose, check_for_pexpect
302 302 )
303 303 print_line()
304 304 print_raw("BUILDING IPYTHON")
305 305 print_status('python', sys.version)
306 306 print_status('platform', sys.platform)
307 307 if sys.platform == 'win32':
308 308 print_status('Windows version', sys.getwindowsversion())
309 309
310 310 print_raw("")
311 311 print_raw("OPTIONAL DEPENDENCIES")
312 312
313 313 check_for_zopeinterface()
314 314 check_for_twisted()
315 315 check_for_foolscap()
316 316 check_for_pyopenssl()
317 317 check_for_sphinx()
318 318 check_for_pygments()
319 319 check_for_nose()
320 320 check_for_pexpect()
General Comments 0
You need to be logged in to leave comments. Login now