##// END OF EJS Templates
Several small fixes during code review with Brian....
Fernando Perez -
Show More
@@ -1,995 +1,995 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Prefiltering components.
5 5
6 6 Prefilters transform user input before it is exec'd by Python. These
7 7 transforms are used to implement additional syntax such as !ls and %magic.
8 8
9 9 Authors:
10 10
11 11 * Brian Granger
12 12 * Fernando Perez
13 13 * Dan Milstein
14 14 * Ville Vainio
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2008-2009 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27
28 28 import __builtin__
29 29 import codeop
30 30 import keyword
31 31 import os
32 32 import re
33 33 import sys
34 34
35 35 from IPython.core.alias import AliasManager
36 36 from IPython.core.autocall import IPyAutocall
37 37 from IPython.core.component import Component
38 38 from IPython.core.splitinput import split_user_input
39 39 from IPython.core.page import page
40 40
41 41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
42 from IPython.utils.genutils import make_quoted_expr
42 from IPython.utils.genutils import make_quoted_expr, Term
43 43 from IPython.utils.autoattr import auto_attr
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Global utilities, errors and constants
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # Warning, these cannot be changed unless various regular expressions
50 50 # are updated in a number of places. Not great, but at least we told you.
51 51 ESC_SHELL = '!'
52 52 ESC_SH_CAP = '!!'
53 53 ESC_HELP = '?'
54 54 ESC_MAGIC = '%'
55 55 ESC_QUOTE = ','
56 56 ESC_QUOTE2 = ';'
57 57 ESC_PAREN = '/'
58 58
59 59
60 60 class PrefilterError(Exception):
61 61 pass
62 62
63 63
64 64 # RegExp to identify potential function names
65 65 re_fun_name = re.compile(r'[a-zA-Z_]([a-zA-Z0-9_.]*) *$')
66 66
67 67 # RegExp to exclude strings with this start from autocalling. In
68 68 # particular, all binary operators should be excluded, so that if foo is
69 69 # callable, foo OP bar doesn't become foo(OP bar), which is invalid. The
70 70 # characters '!=()' don't need to be checked for, as the checkPythonChars
71 71 # routine explicitely does so, to catch direct calls and rebindings of
72 72 # existing names.
73 73
74 74 # Warning: the '-' HAS TO BE AT THE END of the first group, otherwise
75 75 # it affects the rest of the group in square brackets.
76 76 re_exclude_auto = re.compile(r'^[,&^\|\*/\+-]'
77 77 r'|^is |^not |^in |^and |^or ')
78 78
79 79 # try to catch also methods for stuff in lists/tuples/dicts: off
80 80 # (experimental). For this to work, the line_split regexp would need
81 81 # to be modified so it wouldn't break things at '['. That line is
82 82 # nasty enough that I shouldn't change it until I can test it _well_.
83 83 #self.re_fun_name = re.compile (r'[a-zA-Z_]([a-zA-Z0-9_.\[\]]*) ?$')
84 84
85 85
86 86 # Handler Check Utilities
87 87 def is_shadowed(identifier, ip):
88 88 """Is the given identifier defined in one of the namespaces which shadow
89 89 the alias and magic namespaces? Note that an identifier is different
90 90 than ifun, because it can not contain a '.' character."""
91 91 # This is much safer than calling ofind, which can change state
92 92 return (identifier in ip.user_ns \
93 93 or identifier in ip.internal_ns \
94 94 or identifier in ip.ns_table['builtin'])
95 95
96 96
97 97 #-----------------------------------------------------------------------------
98 98 # The LineInfo class used throughout
99 99 #-----------------------------------------------------------------------------
100 100
101 101
102 102 class LineInfo(object):
103 103 """A single line of input and associated info.
104 104
105 105 Includes the following as properties:
106 106
107 107 line
108 108 The original, raw line
109 109
110 110 continue_prompt
111 111 Is this line a continuation in a sequence of multiline input?
112 112
113 113 pre
114 114 The initial esc character or whitespace.
115 115
116 116 pre_char
117 117 The escape character(s) in pre or the empty string if there isn't one.
118 118 Note that '!!' is a possible value for pre_char. Otherwise it will
119 119 always be a single character.
120 120
121 121 pre_whitespace
122 122 The leading whitespace from pre if it exists. If there is a pre_char,
123 123 this is just ''.
124 124
125 125 ifun
126 126 The 'function part', which is basically the maximal initial sequence
127 127 of valid python identifiers and the '.' character. This is what is
128 128 checked for alias and magic transformations, used for auto-calling,
129 129 etc.
130 130
131 131 the_rest
132 132 Everything else on the line.
133 133 """
134 134 def __init__(self, line, continue_prompt):
135 135 self.line = line
136 136 self.continue_prompt = continue_prompt
137 137 self.pre, self.ifun, self.the_rest = split_user_input(line)
138 138
139 139 self.pre_char = self.pre.strip()
140 140 if self.pre_char:
141 141 self.pre_whitespace = '' # No whitespace allowd before esc chars
142 142 else:
143 143 self.pre_whitespace = self.pre
144 144
145 145 self._oinfo = None
146 146
147 147 def ofind(self, ip):
148 148 """Do a full, attribute-walking lookup of the ifun in the various
149 149 namespaces for the given IPython InteractiveShell instance.
150 150
151 151 Return a dict with keys: found,obj,ospace,ismagic
152 152
153 153 Note: can cause state changes because of calling getattr, but should
154 154 only be run if autocall is on and if the line hasn't matched any
155 155 other, less dangerous handlers.
156 156
157 157 Does cache the results of the call, so can be called multiple times
158 158 without worrying about *further* damaging state.
159 159 """
160 160 if not self._oinfo:
161 161 self._oinfo = ip._ofind(self.ifun)
162 162 return self._oinfo
163 163
164 164 def __str__(self):
165 165 return "Lineinfo [%s|%s|%s]" %(self.pre,self.ifun,self.the_rest)
166 166
167 167
168 168 #-----------------------------------------------------------------------------
169 169 # Main Prefilter manager
170 170 #-----------------------------------------------------------------------------
171 171
172 172
173 173 class PrefilterManager(Component):
174 174 """Main prefilter component.
175 175
176 176 The IPython prefilter is run on all user input before it is run. The
177 177 prefilter consumes lines of input and produces transformed lines of
178 178 input.
179 179
180 180 The iplementation consists of two phases:
181 181
182 182 1. Transformers
183 183 2. Checkers and handlers
184 184
185 185 Over time, we plan on deprecating the checkers and handlers and doing
186 186 everything in the transformers.
187 187
188 188 The transformers are instances of :class:`PrefilterTransformer` and have
189 189 a single method :meth:`transform` that takes a line and returns a
190 190 transformed line. The transformation can be accomplished using any
191 191 tool, but our current ones use regular expressions for speed. We also
192 192 ship :mod:`pyparsing` in :mod:`IPython.external` for use in transformers.
193 193
194 194 After all the transformers have been run, the line is fed to the checkers,
195 195 which are instances of :class:`PrefilterChecker`. The line is passed to
196 196 the :meth:`check` method, which either returns `None` or a
197 197 :class:`PrefilterHandler` instance. If `None` is returned, the other
198 198 checkers are tried. If an :class:`PrefilterHandler` instance is returned,
199 199 the line is passed to the :meth:`handle` method of the returned
200 200 handler and no further checkers are tried.
201 201
202 202 Both transformers and checkers have a `priority` attribute, that determines
203 203 the order in which they are called. Smaller priorities are tried first.
204 204
205 205 Both transformers and checkers also have `enabled` attribute, which is
206 206 a boolean that determines if the instance is used.
207 207
208 208 Users or developers can change the priority or enabled attribute of
209 209 transformers or checkers, but they must call the :meth:`sort_checkers`
210 210 or :meth:`sort_transformers` method after changing the priority.
211 211 """
212 212
213 213 multi_line_specials = CBool(True, config=True)
214 214
215 215 def __init__(self, parent, config=None):
216 216 super(PrefilterManager, self).__init__(parent, config=config)
217 217 self.init_transformers()
218 218 self.init_handlers()
219 219 self.init_checkers()
220 220
221 221 @auto_attr
222 222 def shell(self):
223 223 return Component.get_instances(
224 224 root=self.root,
225 225 klass='IPython.core.iplib.InteractiveShell')[0]
226 226
227 227 #-------------------------------------------------------------------------
228 228 # API for managing transformers
229 229 #-------------------------------------------------------------------------
230 230
231 231 def init_transformers(self):
232 232 """Create the default transformers."""
233 233 self._transformers = []
234 234 for transformer_cls in _default_transformers:
235 235 transformer_cls(self, config=self.config)
236 236
237 237 def sort_transformers(self):
238 238 """Sort the transformers by priority.
239 239
240 240 This must be called after the priority of a transformer is changed.
241 241 The :meth:`register_transformer` method calls this automatically.
242 242 """
243 243 self._transformers.sort(cmp=lambda x,y: x.priority-y.priority)
244 244
245 245 @property
246 246 def transformers(self):
247 247 """Return a list of checkers, sorted by priority."""
248 248 return self._transformers
249 249
250 250 def register_transformer(self, transformer):
251 251 """Register a transformer instance."""
252 252 if transformer not in self._transformers:
253 253 self._transformers.append(transformer)
254 254 self.sort_transformers()
255 255
256 256 def unregister_transformer(self, transformer):
257 257 """Unregister a transformer instance."""
258 258 if transformer in self._transformers:
259 259 self._transformers.remove(transformer)
260 260
261 261 #-------------------------------------------------------------------------
262 262 # API for managing checkers
263 263 #-------------------------------------------------------------------------
264 264
265 265 def init_checkers(self):
266 266 """Create the default checkers."""
267 267 self._checkers = []
268 268 for checker in _default_checkers:
269 269 checker(self, config=self.config)
270 270
271 271 def sort_checkers(self):
272 272 """Sort the checkers by priority.
273 273
274 274 This must be called after the priority of a checker is changed.
275 275 The :meth:`register_checker` method calls this automatically.
276 276 """
277 277 self._checkers.sort(cmp=lambda x,y: x.priority-y.priority)
278 278
279 279 @property
280 280 def checkers(self):
281 281 """Return a list of checkers, sorted by priority."""
282 282 return self._checkers
283 283
284 284 def register_checker(self, checker):
285 285 """Register a checker instance."""
286 286 if checker not in self._checkers:
287 287 self._checkers.append(checker)
288 288 self.sort_checkers()
289 289
290 290 def unregister_checker(self, checker):
291 291 """Unregister a checker instance."""
292 292 if checker in self._checkers:
293 293 self._checkers.remove(checker)
294 294
295 295 #-------------------------------------------------------------------------
296 296 # API for managing checkers
297 297 #-------------------------------------------------------------------------
298 298
299 299 def init_handlers(self):
300 300 """Create the default handlers."""
301 301 self._handlers = {}
302 302 self._esc_handlers = {}
303 303 for handler in _default_handlers:
304 304 handler(self, config=self.config)
305 305
306 306 @property
307 307 def handlers(self):
308 308 """Return a dict of all the handlers."""
309 309 return self._handlers
310 310
311 311 def register_handler(self, name, handler, esc_strings):
312 312 """Register a handler instance by name with esc_strings."""
313 313 self._handlers[name] = handler
314 314 for esc_str in esc_strings:
315 315 self._esc_handlers[esc_str] = handler
316 316
317 317 def unregister_handler(self, name, handler, esc_strings):
318 318 """Unregister a handler instance by name with esc_strings."""
319 319 try:
320 320 del self._handlers[name]
321 321 except KeyError:
322 322 pass
323 323 for esc_str in esc_strings:
324 324 h = self._esc_handlers.get(esc_str)
325 325 if h is handler:
326 326 del self._esc_handlers[esc_str]
327 327
328 328 def get_handler_by_name(self, name):
329 329 """Get a handler by its name."""
330 330 return self._handlers.get(name)
331 331
332 332 def get_handler_by_esc(self, esc_str):
333 333 """Get a handler by its escape string."""
334 334 return self._esc_handlers.get(esc_str)
335 335
336 336 #-------------------------------------------------------------------------
337 337 # Main prefiltering API
338 338 #-------------------------------------------------------------------------
339 339
340 340 def prefilter_line_info(self, line_info):
341 341 """Prefilter a line that has been converted to a LineInfo object.
342 342
343 343 This implements the checker/handler part of the prefilter pipe.
344 344 """
345 345 # print "prefilter_line_info: ", line_info
346 346 handler = self.find_handler(line_info)
347 347 return handler.handle(line_info)
348 348
349 349 def find_handler(self, line_info):
350 350 """Find a handler for the line_info by trying checkers."""
351 351 for checker in self.checkers:
352 352 if checker.enabled:
353 353 handler = checker.check(line_info)
354 354 if handler:
355 355 return handler
356 356 return self.get_handler_by_name('normal')
357 357
358 358 def transform_line(self, line, continue_prompt):
359 359 """Calls the enabled transformers in order of increasing priority."""
360 360 for transformer in self.transformers:
361 361 if transformer.enabled:
362 362 line = transformer.transform(line, continue_prompt)
363 363 return line
364 364
365 365 def prefilter_line(self, line, continue_prompt):
366 366 """Prefilter a single input line as text.
367 367
368 368 This method prefilters a single line of text by calling the
369 369 transformers and then the checkers/handlers.
370 370 """
371 371
372 372 # print "prefilter_line: ", line, continue_prompt
373 373 # All handlers *must* return a value, even if it's blank ('').
374 374
375 375 # Lines are NOT logged here. Handlers should process the line as
376 376 # needed, update the cache AND log it (so that the input cache array
377 377 # stays synced).
378 378
379 379 # save the line away in case we crash, so the post-mortem handler can
380 380 # record it
381 381 self.shell._last_input_line = line
382 382
383 383 if not line:
384 384 # Return immediately on purely empty lines, so that if the user
385 385 # previously typed some whitespace that started a continuation
386 386 # prompt, he can break out of that loop with just an empty line.
387 387 # This is how the default python prompt works.
388 388
389 389 # Only return if the accumulated input buffer was just whitespace!
390 390 if ''.join(self.shell.buffer).isspace():
391 391 self.shell.buffer[:] = []
392 392 return ''
393 393
394 394 # At this point, we invoke our transformers.
395 395 if not continue_prompt or (continue_prompt and self.multi_line_specials):
396 396 line = self.transform_line(line, continue_prompt)
397 397
398 398 # Now we compute line_info for the checkers and handlers
399 399 line_info = LineInfo(line, continue_prompt)
400 400
401 401 # the input history needs to track even empty lines
402 402 stripped = line.strip()
403 403
404 404 normal_handler = self.get_handler_by_name('normal')
405 405 if not stripped:
406 406 if not continue_prompt:
407 407 self.shell.outputcache.prompt_count -= 1
408 408
409 409 return normal_handler.handle(line_info)
410 410
411 411 # special handlers are only allowed for single line statements
412 412 if continue_prompt and not self.multi_line_specials:
413 413 return normal_handler.handle(line_info)
414 414
415 415 prefiltered = self.prefilter_line_info(line_info)
416 416 # print "prefiltered line: %r" % prefiltered
417 417 return prefiltered
418 418
419 419 def prefilter_lines(self, lines, continue_prompt):
420 420 """Prefilter multiple input lines of text.
421 421
422 422 This is the main entry point for prefiltering multiple lines of
423 423 input. This simply calls :meth:`prefilter_line` for each line of
424 424 input.
425 425
426 426 This covers cases where there are multiple lines in the user entry,
427 427 which is the case when the user goes back to a multiline history
428 428 entry and presses enter.
429 429 """
430 430 out = []
431 431 for line in lines.rstrip('\n').split('\n'):
432 432 out.append(self.prefilter_line(line, continue_prompt))
433 433 return '\n'.join(out)
434 434
435 435
436 436 #-----------------------------------------------------------------------------
437 437 # Prefilter transformers
438 438 #-----------------------------------------------------------------------------
439 439
440 440
441 441 class PrefilterTransformer(Component):
442 442 """Transform a line of user input."""
443 443
444 444 priority = Int(100, config=True)
445 445 shell = Any
446 446 prefilter_manager = Any
447 447 enabled = Bool(True, config=True)
448 448
449 449 def __init__(self, parent, config=None):
450 450 super(PrefilterTransformer, self).__init__(parent, config=config)
451 451 self.prefilter_manager.register_transformer(self)
452 452
453 453 @auto_attr
454 454 def shell(self):
455 455 return Component.get_instances(
456 456 root=self.root,
457 457 klass='IPython.core.iplib.InteractiveShell')[0]
458 458
459 459 @auto_attr
460 460 def prefilter_manager(self):
461 461 return PrefilterManager.get_instances(root=self.root)[0]
462 462
463 463 def transform(self, line, continue_prompt):
464 464 """Transform a line, returning the new one."""
465 465 return None
466 466
467 467 def __repr__(self):
468 468 return "<%s(priority=%r, enabled=%r)>" % (
469 469 self.__class__.__name__, self.priority, self.enabled)
470 470
471 471
472 472 _assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
473 473 r'\s*=\s*!(?P<cmd>.*)')
474 474
475 475
476 476 class AssignSystemTransformer(PrefilterTransformer):
477 477 """Handle the `files = !ls` syntax."""
478 478
479 479 priority = Int(100, config=True)
480 480
481 481 def transform(self, line, continue_prompt):
482 482 m = _assign_system_re.match(line)
483 483 if m is not None:
484 484 cmd = m.group('cmd')
485 485 lhs = m.group('lhs')
486 486 expr = make_quoted_expr("sc -l =%s" % cmd)
487 487 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
488 488 return new_line
489 489 return line
490 490
491 491
492 492 _assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
493 493 r'\s*=\s*%(?P<cmd>.*)')
494 494
495 495 class AssignMagicTransformer(PrefilterTransformer):
496 496 """Handle the `a = %who` syntax."""
497 497
498 498 priority = Int(200, config=True)
499 499
500 500 def transform(self, line, continue_prompt):
501 501 m = _assign_magic_re.match(line)
502 502 if m is not None:
503 503 cmd = m.group('cmd')
504 504 lhs = m.group('lhs')
505 505 expr = make_quoted_expr(cmd)
506 506 new_line = '%s = get_ipython().magic(%s)' % (lhs, expr)
507 507 return new_line
508 508 return line
509 509
510 510
511 511 #-----------------------------------------------------------------------------
512 512 # Prefilter checkers
513 513 #-----------------------------------------------------------------------------
514 514
515 515
516 516 class PrefilterChecker(Component):
517 517 """Inspect an input line and return a handler for that line."""
518 518
519 519 priority = Int(100, config=True)
520 520 shell = Any
521 521 prefilter_manager = Any
522 522 enabled = Bool(True, config=True)
523 523
524 524 def __init__(self, parent, config=None):
525 525 super(PrefilterChecker, self).__init__(parent, config=config)
526 526 self.prefilter_manager.register_checker(self)
527 527
528 528 @auto_attr
529 529 def shell(self):
530 530 return Component.get_instances(
531 531 root=self.root,
532 532 klass='IPython.core.iplib.InteractiveShell')[0]
533 533
534 534 @auto_attr
535 535 def prefilter_manager(self):
536 536 return PrefilterManager.get_instances(root=self.root)[0]
537 537
538 538 def check(self, line_info):
539 539 """Inspect line_info and return a handler instance or None."""
540 540 return None
541 541
542 542 def __repr__(self):
543 543 return "<%s(priority=%r, enabled=%r)>" % (
544 544 self.__class__.__name__, self.priority, self.enabled)
545 545
546 546
547 547 class EmacsChecker(PrefilterChecker):
548 548
549 549 priority = Int(100, config=True)
550 550 enabled = Bool(False, config=True)
551 551
552 552 def check(self, line_info):
553 553 "Emacs ipython-mode tags certain input lines."
554 554 if line_info.line.endswith('# PYTHON-MODE'):
555 555 return self.prefilter_manager.get_handler_by_name('emacs')
556 556 else:
557 557 return None
558 558
559 559
560 560 class ShellEscapeChecker(PrefilterChecker):
561 561
562 562 priority = Int(200, config=True)
563 563
564 564 def check(self, line_info):
565 565 if line_info.line.lstrip().startswith(ESC_SHELL):
566 566 return self.prefilter_manager.get_handler_by_name('shell')
567 567
568 568
569 569 class IPyAutocallChecker(PrefilterChecker):
570 570
571 571 priority = Int(300, config=True)
572 572
573 573 def check(self, line_info):
574 574 "Instances of IPyAutocall in user_ns get autocalled immediately"
575 575 obj = self.shell.user_ns.get(line_info.ifun, None)
576 576 if isinstance(obj, IPyAutocall):
577 577 obj.set_ip(self.shell)
578 578 return self.prefilter_manager.get_handler_by_name('auto')
579 579 else:
580 580 return None
581 581
582 582
583 583 class MultiLineMagicChecker(PrefilterChecker):
584 584
585 585 priority = Int(400, config=True)
586 586
587 587 def check(self, line_info):
588 588 "Allow ! and !! in multi-line statements if multi_line_specials is on"
589 589 # Note that this one of the only places we check the first character of
590 590 # ifun and *not* the pre_char. Also note that the below test matches
591 591 # both ! and !!.
592 592 if line_info.continue_prompt \
593 593 and self.prefilter_manager.multi_line_specials:
594 594 if line_info.ifun.startswith(ESC_MAGIC):
595 595 return self.prefilter_manager.get_handler_by_name('magic')
596 596 else:
597 597 return None
598 598
599 599
600 600 class EscCharsChecker(PrefilterChecker):
601 601
602 602 priority = Int(500, config=True)
603 603
604 604 def check(self, line_info):
605 605 """Check for escape character and return either a handler to handle it,
606 606 or None if there is no escape char."""
607 607 if line_info.line[-1] == ESC_HELP \
608 608 and line_info.pre_char != ESC_SHELL \
609 609 and line_info.pre_char != ESC_SH_CAP:
610 610 # the ? can be at the end, but *not* for either kind of shell escape,
611 611 # because a ? can be a vaild final char in a shell cmd
612 612 return self.prefilter_manager.get_handler_by_name('help')
613 613 else:
614 614 # This returns None like it should if no handler exists
615 615 return self.prefilter_manager.get_handler_by_esc(line_info.pre_char)
616 616
617 617
618 618 class AssignmentChecker(PrefilterChecker):
619 619
620 620 priority = Int(600, config=True)
621 621
622 622 def check(self, line_info):
623 623 """Check to see if user is assigning to a var for the first time, in
624 624 which case we want to avoid any sort of automagic / autocall games.
625 625
626 626 This allows users to assign to either alias or magic names true python
627 627 variables (the magic/alias systems always take second seat to true
628 628 python code). E.g. ls='hi', or ls,that=1,2"""
629 629 if line_info.the_rest:
630 630 if line_info.the_rest[0] in '=,':
631 631 return self.prefilter_manager.get_handler_by_name('normal')
632 632 else:
633 633 return None
634 634
635 635
636 636 class AutoMagicChecker(PrefilterChecker):
637 637
638 638 priority = Int(700, config=True)
639 639
640 640 def check(self, line_info):
641 641 """If the ifun is magic, and automagic is on, run it. Note: normal,
642 642 non-auto magic would already have been triggered via '%' in
643 643 check_esc_chars. This just checks for automagic. Also, before
644 644 triggering the magic handler, make sure that there is nothing in the
645 645 user namespace which could shadow it."""
646 646 if not self.shell.automagic or not hasattr(self.shell,'magic_'+line_info.ifun):
647 647 return None
648 648
649 649 # We have a likely magic method. Make sure we should actually call it.
650 650 if line_info.continue_prompt and not self.shell.multi_line_specials:
651 651 return None
652 652
653 653 head = line_info.ifun.split('.',1)[0]
654 654 if is_shadowed(head, self.shell):
655 655 return None
656 656
657 657 return self.prefilter_manager.get_handler_by_name('magic')
658 658
659 659
660 660 class AliasChecker(PrefilterChecker):
661 661
662 662 priority = Int(800, config=True)
663 663
664 664 @auto_attr
665 665 def alias_manager(self):
666 666 return AliasManager.get_instances(root=self.root)[0]
667 667
668 668 def check(self, line_info):
669 669 "Check if the initital identifier on the line is an alias."
670 670 # Note: aliases can not contain '.'
671 671 head = line_info.ifun.split('.',1)[0]
672 672 if line_info.ifun not in self.alias_manager \
673 673 or head not in self.alias_manager \
674 674 or is_shadowed(head, self.shell):
675 675 return None
676 676
677 677 return self.prefilter_manager.get_handler_by_name('alias')
678 678
679 679
680 680 class PythonOpsChecker(PrefilterChecker):
681 681
682 682 priority = Int(900, config=True)
683 683
684 684 def check(self, line_info):
685 685 """If the 'rest' of the line begins with a function call or pretty much
686 686 any python operator, we should simply execute the line (regardless of
687 687 whether or not there's a possible autocall expansion). This avoids
688 688 spurious (and very confusing) geattr() accesses."""
689 689 if line_info.the_rest and line_info.the_rest[0] in '!=()<>,+*/%^&|':
690 690 return self.prefilter_manager.get_handler_by_name('normal')
691 691 else:
692 692 return None
693 693
694 694
695 695 class AutocallChecker(PrefilterChecker):
696 696
697 697 priority = Int(1000, config=True)
698 698
699 699 def check(self, line_info):
700 700 "Check if the initial word/function is callable and autocall is on."
701 701 if not self.shell.autocall:
702 702 return None
703 703
704 704 oinfo = line_info.ofind(self.shell) # This can mutate state via getattr
705 705 if not oinfo['found']:
706 706 return None
707 707
708 708 if callable(oinfo['obj']) \
709 709 and (not re_exclude_auto.match(line_info.the_rest)) \
710 710 and re_fun_name.match(line_info.ifun):
711 711 return self.prefilter_manager.get_handler_by_name('auto')
712 712 else:
713 713 return None
714 714
715 715
716 716 #-----------------------------------------------------------------------------
717 717 # Prefilter handlers
718 718 #-----------------------------------------------------------------------------
719 719
720 720
721 721 class PrefilterHandler(Component):
722 722
723 723 handler_name = Str('normal')
724 724 esc_strings = List([])
725 725 shell = Any
726 726 prefilter_manager = Any
727 727
728 728 def __init__(self, parent, config=None):
729 729 super(PrefilterHandler, self).__init__(parent, config=config)
730 730 self.prefilter_manager.register_handler(
731 731 self.handler_name,
732 732 self,
733 733 self.esc_strings
734 734 )
735 735
736 736 @auto_attr
737 737 def shell(self):
738 738 return Component.get_instances(
739 739 root=self.root,
740 740 klass='IPython.core.iplib.InteractiveShell')[0]
741 741
742 742 @auto_attr
743 743 def prefilter_manager(self):
744 744 return PrefilterManager.get_instances(root=self.root)[0]
745 745
746 746 def handle(self, line_info):
747 747 # print "normal: ", line_info
748 748 """Handle normal input lines. Use as a template for handlers."""
749 749
750 750 # With autoindent on, we need some way to exit the input loop, and I
751 751 # don't want to force the user to have to backspace all the way to
752 752 # clear the line. The rule will be in this case, that either two
753 753 # lines of pure whitespace in a row, or a line of pure whitespace but
754 754 # of a size different to the indent level, will exit the input loop.
755 755 line = line_info.line
756 756 continue_prompt = line_info.continue_prompt
757 757
758 758 if (continue_prompt and self.shell.autoindent and line.isspace() and
759 759 (0 < abs(len(line) - self.shell.indent_current_nsp) <= 2 or
760 760 (self.shell.buffer[-1]).isspace() )):
761 761 line = ''
762 762
763 763 self.shell.log(line, line, continue_prompt)
764 764 return line
765 765
766 766 def __str__(self):
767 767 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
768 768
769 769
770 770 class AliasHandler(PrefilterHandler):
771 771
772 772 handler_name = Str('alias')
773 773
774 774 @auto_attr
775 775 def alias_manager(self):
776 776 return AliasManager.get_instances(root=self.root)[0]
777 777
778 778 def handle(self, line_info):
779 779 """Handle alias input lines. """
780 780 transformed = self.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest)
781 781 # pre is needed, because it carries the leading whitespace. Otherwise
782 782 # aliases won't work in indented sections.
783 783 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
784 784 make_quoted_expr(transformed))
785 785
786 786 self.shell.log(line_info.line, line_out, line_info.continue_prompt)
787 787 return line_out
788 788
789 789
790 790 class ShellEscapeHandler(PrefilterHandler):
791 791
792 792 handler_name = Str('shell')
793 793 esc_strings = List([ESC_SHELL, ESC_SH_CAP])
794 794
795 795 def handle(self, line_info):
796 796 """Execute the line in a shell, empty return value"""
797 797 magic_handler = self.prefilter_manager.get_handler_by_name('magic')
798 798
799 799 line = line_info.line
800 800 if line.lstrip().startswith(ESC_SH_CAP):
801 801 # rewrite LineInfo's line, ifun and the_rest to properly hold the
802 802 # call to %sx and the actual command to be executed, so
803 803 # handle_magic can work correctly. Note that this works even if
804 804 # the line is indented, so it handles multi_line_specials
805 805 # properly.
806 806 new_rest = line.lstrip()[2:]
807 807 line_info.line = '%ssx %s' % (ESC_MAGIC, new_rest)
808 808 line_info.ifun = 'sx'
809 809 line_info.the_rest = new_rest
810 810 return magic_handler.handle(line_info)
811 811 else:
812 812 cmd = line.lstrip().lstrip(ESC_SHELL)
813 813 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
814 814 make_quoted_expr(cmd))
815 815 # update cache/log and return
816 816 self.shell.log(line, line_out, line_info.continue_prompt)
817 817 return line_out
818 818
819 819
820 820 class MagicHandler(PrefilterHandler):
821 821
822 822 handler_name = Str('magic')
823 823 esc_strings = List([ESC_MAGIC])
824 824
825 825 def handle(self, line_info):
826 826 """Execute magic functions."""
827 827 ifun = line_info.ifun
828 828 the_rest = line_info.the_rest
829 829 cmd = '%sget_ipython().magic(%s)' % (line_info.pre_whitespace,
830 830 make_quoted_expr(ifun + " " + the_rest))
831 831 self.shell.log(line_info.line, cmd, line_info.continue_prompt)
832 832 return cmd
833 833
834 834
835 835 class AutoHandler(PrefilterHandler):
836 836
837 837 handler_name = Str('auto')
838 838 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
839 839
840 840 def handle(self, line_info):
841 841 """Hande lines which can be auto-executed, quoting if requested."""
842 842 line = line_info.line
843 843 ifun = line_info.ifun
844 844 the_rest = line_info.the_rest
845 845 pre = line_info.pre
846 846 continue_prompt = line_info.continue_prompt
847 847 obj = line_info.ofind(self)['obj']
848 848
849 849 #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun,the_rest) # dbg
850 850
851 851 # This should only be active for single-line input!
852 852 if continue_prompt:
853 853 self.log(line,line,continue_prompt)
854 854 return line
855 855
856 856 force_auto = isinstance(obj, IPyAutocall)
857 857 auto_rewrite = True
858 858
859 859 if pre == ESC_QUOTE:
860 860 # Auto-quote splitting on whitespace
861 861 newcmd = '%s("%s")' % (ifun,'", "'.join(the_rest.split()) )
862 862 elif pre == ESC_QUOTE2:
863 863 # Auto-quote whole string
864 864 newcmd = '%s("%s")' % (ifun,the_rest)
865 865 elif pre == ESC_PAREN:
866 866 newcmd = '%s(%s)' % (ifun,",".join(the_rest.split()))
867 867 else:
868 868 # Auto-paren.
869 869 # We only apply it to argument-less calls if the autocall
870 870 # parameter is set to 2. We only need to check that autocall is <
871 871 # 2, since this function isn't called unless it's at least 1.
872 872 if not the_rest and (self.shell.autocall < 2) and not force_auto:
873 873 newcmd = '%s %s' % (ifun,the_rest)
874 874 auto_rewrite = False
875 875 else:
876 876 if not force_auto and the_rest.startswith('['):
877 877 if hasattr(obj,'__getitem__'):
878 878 # Don't autocall in this case: item access for an object
879 879 # which is BOTH callable and implements __getitem__.
880 880 newcmd = '%s %s' % (ifun,the_rest)
881 881 auto_rewrite = False
882 882 else:
883 883 # if the object doesn't support [] access, go ahead and
884 884 # autocall
885 885 newcmd = '%s(%s)' % (ifun.rstrip(),the_rest)
886 886 elif the_rest.endswith(';'):
887 887 newcmd = '%s(%s);' % (ifun.rstrip(),the_rest[:-1])
888 888 else:
889 889 newcmd = '%s(%s)' % (ifun.rstrip(), the_rest)
890 890
891 891 if auto_rewrite:
892 892 rw = self.shell.outputcache.prompt1.auto_rewrite() + newcmd
893 893
894 894 try:
895 895 # plain ascii works better w/ pyreadline, on some machines, so
896 896 # we use it and only print uncolored rewrite if we have unicode
897 897 rw = str(rw)
898 898 print >>Term.cout, rw
899 899 except UnicodeEncodeError:
900 900 print "-------------->" + newcmd
901 901
902 902 # log what is now valid Python, not the actual user input (without the
903 903 # final newline)
904 904 self.shell.log(line,newcmd,continue_prompt)
905 905 return newcmd
906 906
907 907
908 908 class HelpHandler(PrefilterHandler):
909 909
910 910 handler_name = Str('help')
911 911 esc_strings = List([ESC_HELP])
912 912
913 913 def handle(self, line_info):
914 914 """Try to get some help for the object.
915 915
916 916 obj? or ?obj -> basic information.
917 917 obj?? or ??obj -> more details.
918 918 """
919 919 normal_handler = self.prefilter_manager.get_handler_by_name('normal')
920 920 line = line_info.line
921 921 # We need to make sure that we don't process lines which would be
922 922 # otherwise valid python, such as "x=1 # what?"
923 923 try:
924 924 codeop.compile_command(line)
925 925 except SyntaxError:
926 926 # We should only handle as help stuff which is NOT valid syntax
927 927 if line[0]==ESC_HELP:
928 928 line = line[1:]
929 929 elif line[-1]==ESC_HELP:
930 930 line = line[:-1]
931 931 self.shell.log(line, '#?'+line, line_info.continue_prompt)
932 932 if line:
933 933 #print 'line:<%r>' % line # dbg
934 934 self.shell.magic_pinfo(line)
935 935 else:
936 936 page(self.shell.usage, screen_lines=self.shell.usable_screen_length)
937 937 return '' # Empty string is needed here!
938 938 except:
939 939 raise
940 940 # Pass any other exceptions through to the normal handler
941 941 return normal_handler.handle(line_info)
942 942 else:
943 943 raise
944 944 # If the code compiles ok, we should handle it normally
945 945 return normal_handler.handle(line_info)
946 946
947 947
948 948 class EmacsHandler(PrefilterHandler):
949 949
950 950 handler_name = Str('emacs')
951 951 esc_strings = List([])
952 952
953 953 def handle(self, line_info):
954 954 """Handle input lines marked by python-mode."""
955 955
956 956 # Currently, nothing is done. Later more functionality can be added
957 957 # here if needed.
958 958
959 959 # The input cache shouldn't be updated
960 960 return line_info.line
961 961
962 962
963 963 #-----------------------------------------------------------------------------
964 964 # Defaults
965 965 #-----------------------------------------------------------------------------
966 966
967 967
968 968 _default_transformers = [
969 969 AssignSystemTransformer,
970 970 AssignMagicTransformer
971 971 ]
972 972
973 973 _default_checkers = [
974 974 EmacsChecker,
975 975 ShellEscapeChecker,
976 976 IPyAutocallChecker,
977 977 MultiLineMagicChecker,
978 978 EscCharsChecker,
979 979 AssignmentChecker,
980 980 AutoMagicChecker,
981 981 AliasChecker,
982 982 PythonOpsChecker,
983 983 AutocallChecker
984 984 ]
985 985
986 986 _default_handlers = [
987 987 PrefilterHandler,
988 988 AliasHandler,
989 989 ShellEscapeHandler,
990 990 MagicHandler,
991 991 AutoHandler,
992 992 HelpHandler,
993 993 EmacsHandler
994 994 ]
995 995
@@ -1,457 +1,455 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The ipcluster application.
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 logging
19 19 import os
20 20 import signal
21 21 import sys
22 22
23 23 if os.name=='posix':
24 24 from twisted.scripts._twistd_unix import daemonize
25 25
26 26 from IPython.core import release
27 27 from IPython.external import argparse
28 28 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
29 29 from IPython.utils.importstring import import_item
30 30
31 31 from IPython.kernel.clusterdir import (
32 32 ApplicationWithClusterDir, ClusterDirError, PIDFileError
33 33 )
34 34
35 35 from twisted.internet import reactor, defer
36 36 from twisted.python import log, failure
37 37
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # The ipcluster application
41 41 #-----------------------------------------------------------------------------
42 42
43 43
44 44 # Exit codes for ipcluster
45 45
46 46 # This will be the exit code if the ipcluster appears to be running because
47 47 # a .pid file exists
48 48 ALREADY_STARTED = 10
49 49
50 50 # This will be the exit code if ipcluster stop is run, but there is not .pid
51 51 # file to be found.
52 52 ALREADY_STOPPED = 11
53 53
54 54
55 55 class IPClusterCLLoader(ArgParseConfigLoader):
56 56
57 57 def _add_arguments(self):
58 58 # This has all the common options that all subcommands use
59 59 parent_parser1 = argparse.ArgumentParser(add_help=False)
60 60 parent_parser1.add_argument('--ipython-dir',
61 61 dest='Global.ipython_dir',type=unicode,
62 62 help='Set to override default location of Global.ipython_dir.',
63 63 default=NoConfigDefault,
64 64 metavar='Global.ipython_dir')
65 65 parent_parser1.add_argument('--log-level',
66 66 dest="Global.log_level",type=int,
67 67 help='Set the log level (0,10,20,30,40,50). Default is 30.',
68 68 default=NoConfigDefault,
69 69 metavar='Global.log_level')
70 70
71 71 # This has all the common options that other subcommands use
72 72 parent_parser2 = argparse.ArgumentParser(add_help=False)
73 73 parent_parser2.add_argument('-p','--profile',
74 74 dest='Global.profile',type=unicode,
75 default=NoConfigDefault,
76 75 help='The string name of the profile to be used. This determines '
77 76 'the name of the cluster dir as: cluster_<profile>. The default profile '
78 77 'is named "default". The cluster directory is resolve this way '
79 78 'if the --cluster-dir option is not used.',
80 79 default=NoConfigDefault,
81 80 metavar='Global.profile')
82 81 parent_parser2.add_argument('--cluster-dir',
83 82 dest='Global.cluster_dir',type=unicode,
84 default=NoConfigDefault,
85 83 help='Set the cluster dir. This overrides the logic used by the '
86 84 '--profile option.',
87 85 default=NoConfigDefault,
88 86 metavar='Global.cluster_dir'),
89 87 parent_parser2.add_argument('--work-dir',
90 88 dest='Global.work_dir',type=unicode,
91 89 help='Set the working dir for the process.',
92 90 default=NoConfigDefault,
93 91 metavar='Global.work_dir')
94 92 parent_parser2.add_argument('--log-to-file',
95 93 action='store_true', dest='Global.log_to_file',
96 94 default=NoConfigDefault,
97 95 help='Log to a file in the log directory (default is stdout)'
98 96 )
99 97
100 98 subparsers = self.parser.add_subparsers(
101 99 dest='Global.subcommand',
102 100 title='ipcluster subcommands',
103 101 description='ipcluster has a variety of subcommands. '
104 102 'The general way of running ipcluster is "ipcluster <cmd> '
105 103 ' [options]""',
106 104 help='For more help, type "ipcluster <cmd> -h"')
107 105
108 106 parser_list = subparsers.add_parser(
109 107 'list',
110 108 help='List all clusters in cwd and ipython_dir.',
111 109 parents=[parent_parser1]
112 110 )
113 111
114 112 parser_create = subparsers.add_parser(
115 113 'create',
116 114 help='Create a new cluster directory.',
117 115 parents=[parent_parser1, parent_parser2]
118 116 )
119 117 parser_create.add_argument(
120 118 '--reset-config',
121 119 dest='Global.reset_config', action='store_true',
122 120 default=NoConfigDefault,
123 121 help='Recopy the default config files to the cluster directory. '
124 122 'You will loose any modifications you have made to these files.'
125 123 )
126 124
127 125 parser_start = subparsers.add_parser(
128 126 'start',
129 127 help='Start a cluster.',
130 128 parents=[parent_parser1, parent_parser2]
131 129 )
132 130 parser_start.add_argument(
133 131 '-n', '--number',
134 132 type=int, dest='Global.n',
135 133 default=NoConfigDefault,
136 134 help='The number of engines to start.',
137 135 metavar='Global.n'
138 136 )
139 137 parser_start.add_argument('--clean-logs',
140 138 dest='Global.clean_logs', action='store_true',
141 139 help='Delete old log flies before starting.',
142 140 default=NoConfigDefault
143 141 )
144 142 parser_start.add_argument('--no-clean-logs',
145 143 dest='Global.clean_logs', action='store_false',
146 144 help="Don't delete old log flies before starting.",
147 145 default=NoConfigDefault
148 146 )
149 147 parser_start.add_argument('--daemon',
150 148 dest='Global.daemonize', action='store_true',
151 149 help='Daemonize the ipcluster program. This implies --log-to-file',
152 150 default=NoConfigDefault
153 151 )
154 152 parser_start.add_argument('--no-daemon',
155 153 dest='Global.daemonize', action='store_false',
156 154 help="Dont't daemonize the ipcluster program.",
157 155 default=NoConfigDefault
158 156 )
159 157
160 158 parser_start = subparsers.add_parser(
161 159 'stop',
162 160 help='Stop a cluster.',
163 161 parents=[parent_parser1, parent_parser2]
164 162 )
165 163 parser_start.add_argument('--signal',
166 164 dest='Global.signal', type=int,
167 165 help="The signal number to use in stopping the cluster (default=2).",
168 166 metavar="Global.signal",
169 167 default=NoConfigDefault
170 168 )
171 169
172 170
173 171 default_config_file_name = u'ipcluster_config.py'
174 172
175 173
176 174 class IPClusterApp(ApplicationWithClusterDir):
177 175
178 176 name = u'ipcluster'
179 177 description = 'Start an IPython cluster (controller and engines).'
180 178 config_file_name = default_config_file_name
181 179 default_log_level = logging.INFO
182 180 auto_create_cluster_dir = False
183 181
184 182 def create_default_config(self):
185 183 super(IPClusterApp, self).create_default_config()
186 184 self.default_config.Global.controller_launcher = \
187 185 'IPython.kernel.launcher.LocalControllerLauncher'
188 186 self.default_config.Global.engine_launcher = \
189 187 'IPython.kernel.launcher.LocalEngineSetLauncher'
190 188 self.default_config.Global.n = 2
191 189 self.default_config.Global.reset_config = False
192 190 self.default_config.Global.clean_logs = True
193 191 self.default_config.Global.signal = 2
194 192 self.default_config.Global.daemonize = False
195 193
196 194 def create_command_line_config(self):
197 195 """Create and return a command line config loader."""
198 196 return IPClusterCLLoader(
199 197 description=self.description,
200 198 version=release.version
201 199 )
202 200
203 201 def find_resources(self):
204 202 subcommand = self.command_line_config.Global.subcommand
205 203 if subcommand=='list':
206 204 self.list_cluster_dirs()
207 205 # Exit immediately because there is nothing left to do.
208 206 self.exit()
209 207 elif subcommand=='create':
210 208 self.auto_create_cluster_dir = True
211 209 super(IPClusterApp, self).find_resources()
212 210 elif subcommand=='start' or subcommand=='stop':
213 211 self.auto_create_cluster_dir = False
214 212 try:
215 213 super(IPClusterApp, self).find_resources()
216 214 except ClusterDirError:
217 215 raise ClusterDirError(
218 216 "Could not find a cluster directory. A cluster dir must "
219 217 "be created before running 'ipcluster start'. Do "
220 218 "'ipcluster create -h' or 'ipcluster list -h' for more "
221 219 "information about creating and listing cluster dirs."
222 220 )
223 221
224 222 def list_cluster_dirs(self):
225 223 # Find the search paths
226 224 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
227 225 if cluster_dir_paths:
228 226 cluster_dir_paths = cluster_dir_paths.split(':')
229 227 else:
230 228 cluster_dir_paths = []
231 229 try:
232 230 ipython_dir = self.command_line_config.Global.ipython_dir
233 231 except AttributeError:
234 232 ipython_dir = self.default_config.Global.ipython_dir
235 233 paths = [os.getcwd(), ipython_dir] + \
236 234 cluster_dir_paths
237 235 paths = list(set(paths))
238 236
239 237 self.log.info('Searching for cluster dirs in paths: %r' % paths)
240 238 for path in paths:
241 239 files = os.listdir(path)
242 240 for f in files:
243 241 full_path = os.path.join(path, f)
244 242 if os.path.isdir(full_path) and f.startswith('cluster_'):
245 243 profile = full_path.split('_')[-1]
246 244 start_cmd = '"ipcluster start -n 4 -p %s"' % profile
247 245 print start_cmd + " ==> " + full_path
248 246
249 247 def pre_construct(self):
250 248 # IPClusterApp.pre_construct() is where we cd to the working directory.
251 249 super(IPClusterApp, self).pre_construct()
252 250 config = self.master_config
253 251 try:
254 252 daemon = config.Global.daemonize
255 253 if daemon:
256 254 config.Global.log_to_file = True
257 255 except AttributeError:
258 256 pass
259 257
260 258 def construct(self):
261 259 config = self.master_config
262 260 if config.Global.subcommand=='list':
263 261 pass
264 262 elif config.Global.subcommand=='create':
265 263 self.log.info('Copying default config files to cluster directory '
266 264 '[overwrite=%r]' % (config.Global.reset_config,))
267 265 self.cluster_dir_obj.copy_all_config_files(overwrite=config.Global.reset_config)
268 266 elif config.Global.subcommand=='start':
269 267 self.start_logging()
270 268 reactor.callWhenRunning(self.start_launchers)
271 269
272 270 def start_launchers(self):
273 271 config = self.master_config
274 272
275 273 # Create the launchers. In both bases, we set the work_dir of
276 274 # the launcher to the cluster_dir. This is where the launcher's
277 275 # subprocesses will be launched. It is not where the controller
278 276 # and engine will be launched.
279 277 el_class = import_item(config.Global.engine_launcher)
280 278 self.engine_launcher = el_class(
281 279 work_dir=self.cluster_dir, config=config
282 280 )
283 281 cl_class = import_item(config.Global.controller_launcher)
284 282 self.controller_launcher = cl_class(
285 283 work_dir=self.cluster_dir, config=config
286 284 )
287 285
288 286 # Setup signals
289 287 signal.signal(signal.SIGINT, self.sigint_handler)
290 288
291 289 # Setup the observing of stopping. If the controller dies, shut
292 290 # everything down as that will be completely fatal for the engines.
293 291 d1 = self.controller_launcher.observe_stop()
294 292 d1.addCallback(self.stop_launchers)
295 293 # But, we don't monitor the stopping of engines. An engine dying
296 294 # is just fine and in principle a user could start a new engine.
297 295 # Also, if we did monitor engine stopping, it is difficult to
298 296 # know what to do when only some engines die. Currently, the
299 297 # observing of engine stopping is inconsistent. Some launchers
300 298 # might trigger on a single engine stopping, other wait until
301 299 # all stop. TODO: think more about how to handle this.
302 300
303 301 # Start the controller and engines
304 302 self._stopping = False # Make sure stop_launchers is not called 2x.
305 303 d = self.start_controller()
306 304 d.addCallback(self.start_engines)
307 305 d.addCallback(self.startup_message)
308 306 # If the controller or engines fail to start, stop everything
309 307 d.addErrback(self.stop_launchers)
310 308 return d
311 309
312 310 def startup_message(self, r=None):
313 311 log.msg("IPython cluster: started")
314 312 return r
315 313
316 314 def start_controller(self, r=None):
317 315 # log.msg("In start_controller")
318 316 config = self.master_config
319 317 d = self.controller_launcher.start(
320 318 cluster_dir=config.Global.cluster_dir
321 319 )
322 320 return d
323 321
324 322 def start_engines(self, r=None):
325 323 # log.msg("In start_engines")
326 324 config = self.master_config
327 325 d = self.engine_launcher.start(
328 326 config.Global.n,
329 327 cluster_dir=config.Global.cluster_dir
330 328 )
331 329 return d
332 330
333 331 def stop_controller(self, r=None):
334 332 # log.msg("In stop_controller")
335 333 if self.controller_launcher.running:
336 334 d = self.controller_launcher.stop()
337 335 d.addErrback(self.log_err)
338 336 return d
339 337 else:
340 338 return defer.succeed(None)
341 339
342 340 def stop_engines(self, r=None):
343 341 # log.msg("In stop_engines")
344 342 if self.engine_launcher.running:
345 343 d = self.engine_launcher.stop()
346 344 d.addErrback(self.log_err)
347 345 return d
348 346 else:
349 347 return defer.succeed(None)
350 348
351 349 def log_err(self, f):
352 350 log.msg(f.getTraceback())
353 351 return None
354 352
355 353 def stop_launchers(self, r=None):
356 354 if not self._stopping:
357 355 self._stopping = True
358 356 if isinstance(r, failure.Failure):
359 357 log.msg('Unexpected error in ipcluster:')
360 358 log.msg(r.getTraceback())
361 359 log.msg("IPython cluster: stopping")
362 360 d= self.stop_engines()
363 361 d2 = self.stop_controller()
364 362 # Wait a few seconds to let things shut down.
365 363 reactor.callLater(3.0, reactor.stop)
366 364
367 365 def sigint_handler(self, signum, frame):
368 366 self.stop_launchers()
369 367
370 368 def start_logging(self):
371 369 # Remove old log files of the controller and engine
372 370 if self.master_config.Global.clean_logs:
373 371 log_dir = self.master_config.Global.log_dir
374 372 for f in os.listdir(log_dir):
375 373 if f.startswith('ipengine' + '-'):
376 374 if f.endswith('.log') or f.endswith('.out') or f.endswith('.err'):
377 375 os.remove(os.path.join(log_dir, f))
378 376 if f.startswith('ipcontroller' + '-'):
379 377 if f.endswith('.log') or f.endswith('.out') or f.endswith('.err'):
380 378 os.remove(os.path.join(log_dir, f))
381 379 # This will remote old log files for ipcluster itself
382 380 super(IPClusterApp, self).start_logging()
383 381
384 382 def start_app(self):
385 383 """Start the application, depending on what subcommand is used."""
386 384 subcmd = self.master_config.Global.subcommand
387 385 if subcmd=='create' or subcmd=='list':
388 386 return
389 387 elif subcmd=='start':
390 388 self.start_app_start()
391 389 elif subcmd=='stop':
392 390 self.start_app_stop()
393 391
394 392 def start_app_start(self):
395 393 """Start the app for the start subcommand."""
396 394 config = self.master_config
397 395 # First see if the cluster is already running
398 396 try:
399 397 pid = self.get_pid_from_file()
400 398 except PIDFileError:
401 399 pass
402 400 else:
403 401 self.log.critical(
404 402 'Cluster is already running with [pid=%s]. '
405 403 'use "ipcluster stop" to stop the cluster.' % pid
406 404 )
407 405 # Here I exit with a unusual exit status that other processes
408 406 # can watch for to learn how I existed.
409 407 self.exit(ALREADY_STARTED)
410 408
411 409 # Now log and daemonize
412 410 self.log.info(
413 411 'Starting ipcluster with [daemon=%r]' % config.Global.daemonize
414 412 )
415 413 if config.Global.daemonize:
416 414 if os.name=='posix':
417 415 daemonize()
418 416
419 417 # Now write the new pid file AFTER our new forked pid is active.
420 418 self.write_pid_file()
421 419 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
422 420 reactor.run()
423 421
424 422 def start_app_stop(self):
425 423 """Start the app for the stop subcommand."""
426 424 config = self.master_config
427 425 try:
428 426 pid = self.get_pid_from_file()
429 427 except PIDFileError:
430 428 self.log.critical(
431 429 'Problem reading pid file, cluster is probably not running.'
432 430 )
433 431 # Here I exit with a unusual exit status that other processes
434 432 # can watch for to learn how I existed.
435 433 self.exit(ALREADY_STOPPED)
436 434 else:
437 435 if os.name=='posix':
438 436 sig = config.Global.signal
439 437 self.log.info(
440 438 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
441 439 )
442 440 os.kill(pid, sig)
443 441 elif os.name=='nt':
444 442 # As of right now, we don't support daemonize on Windows, so
445 443 # stop will not do anything. Minimally, it should clean up the
446 444 # old .pid files.
447 445 self.remove_pid_file()
448 446
449 447 def launch_new_instance():
450 448 """Create and run the IPython cluster."""
451 449 app = IPClusterApp()
452 450 app.start()
453 451
454 452
455 453 if __name__ == '__main__':
456 454 launch_new_instance()
457 455
@@ -1,91 +1,93 b''
1 1 # Makefile for Sphinx documentation
2 2 #
3 3
4 4 # You can set these variables from the command line.
5 5 SPHINXOPTS =
6 6 SPHINXBUILD = sphinx-build
7 7 PAPER =
8 8 SRCDIR = source
9 9
10 10 # Internal variables.
11 11 PAPEROPT_a4 = -D latex_paper_size=a4
12 12 PAPEROPT_letter = -D latex_paper_size=letter
13 13 ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR)
14 14
15 15 .PHONY: help clean html web pickle htmlhelp latex changes linkcheck api
16 16
17 default: html
18
17 19 help:
18 20 @echo "Please use \`make <target>' where <target> is one of"
19 21 @echo " html to make standalone HTML files"
20 22 @echo " pickle to make pickle files (usable by e.g. sphinx-web)"
21 23 @echo " htmlhelp to make HTML files and a HTML help project"
22 24 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
23 25 @echo " changes to make an overview over all changed/added/deprecated items"
24 26 @echo " linkcheck to check all external links for integrity"
25 27 @echo
26 28 @echo "Compound utility targets:"
27 29 @echo "pdf latex and then runs the PDF generation"
28 30 @echo "all html and pdf"
29 31 @echo "dist all, and then puts the results in dist/"
30 32
31 33 clean:
32 34 -rm -rf build/* dist/* $(SRCDIR)/api/generated
33 35
34 36 pdf: latex
35 37 cd build/latex && make all-pdf
36 38
37 39 all: html pdf
38 40
39 41 dist: clean all
40 42 mkdir -p dist
41 43 ln build/latex/ipython.pdf dist/
42 44 cp -al build/html dist/
43 45 @echo "Build finished. Final docs are in dist/"
44 46
45 47 html: api
46 48 mkdir -p build/html build/doctrees
47 49 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
48 50 @echo
49 51 @echo "Build finished. The HTML pages are in build/html."
50 52
51 53 api:
52 54 python autogen_api.py
53 55 @echo "Build API docs finished."
54 56
55 57 pickle:
56 58 mkdir -p build/pickle build/doctrees
57 59 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
58 60 @echo
59 61 @echo "Build finished; now you can process the pickle files or run"
60 62 @echo " sphinx-web build/pickle"
61 63 @echo "to start the sphinx-web server."
62 64
63 65 web: pickle
64 66
65 67 htmlhelp:
66 68 mkdir -p build/htmlhelp build/doctrees
67 69 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
68 70 @echo
69 71 @echo "Build finished; now you can run HTML Help Workshop with the" \
70 72 ".hhp project file in build/htmlhelp."
71 73
72 74 latex:
73 75 mkdir -p build/latex build/doctrees
74 76 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
75 77 @echo
76 78 @echo "Build finished; the LaTeX files are in build/latex."
77 79 @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
78 80 "run these through (pdf)latex."
79 81
80 82 changes:
81 83 mkdir -p build/changes build/doctrees
82 84 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
83 85 @echo
84 86 @echo "The overview file is in build/changes."
85 87
86 88 linkcheck:
87 89 mkdir -p build/linkcheck build/doctrees
88 90 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
89 91 @echo
90 92 @echo "Link check complete; look for any errors in the above output " \
91 93 "or in build/linkcheck/output.txt."
@@ -1,42 +1,43 b''
1 1 #!/usr/bin/env python
2 2 """Script to auto-generate our API docs.
3 3 """
4 4 # stdlib imports
5 5 import os
6 6 import sys
7 7
8 8 # local imports
9 9 sys.path.append(os.path.abspath('sphinxext'))
10 10 from apigen import ApiDocWriter
11 11
12 12 #*****************************************************************************
13 13 if __name__ == '__main__':
14 14 pjoin = os.path.join
15 15 package = 'IPython'
16 16 outdir = pjoin('source','api','generated')
17 17 docwriter = ApiDocWriter(package,rst_extension='.txt')
18 18 # You have to escape the . here because . is a special char for regexps.
19 19 # You must do make clean if you change this!
20 20 docwriter.package_skip_patterns += [r'\.fixes$',
21 21 r'\.external$',
22 22 r'\.extensions',
23 23 r'\.kernel\.config',
24 24 r'\.attic',
25 25 r'\.quarantine',
26 26 r'\.deathrow',
27 27 r'\.config\.default',
28 28 r'\.config\.profile',
29 29 r'\.frontend',
30 30 r'\.gui'
31 31 ]
32 32 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
33 33 r'\.cocoa',
34 34 r'\.ipdoctest',
35 35 r'\.Gnuplot',
36 36 r'\.frontend\.process\.winprocess',
37 r'\.Shell',
37 38 ]
38 39 docwriter.write_api_docs(outdir)
39 40 docwriter.write_index(outdir, 'gen',
40 41 relative_to = pjoin('source','api')
41 42 )
42 43 print '%d files written' % len(docwriter.written_modules)
@@ -1,192 +1,192 b''
1 1 # -*- coding: utf-8 -*-
2 2 #
3 3 # IPython documentation build configuration file.
4 4
5 5 # NOTE: This file has been edited manually from the auto-generated one from
6 6 # sphinx. Do NOT delete and re-generate. If any changes from sphinx are
7 7 # needed, generate a scratch one and merge by hand any new fields needed.
8 8
9 9 #
10 10 # This file is execfile()d with the current directory set to its containing dir.
11 11 #
12 12 # The contents of this file are pickled, so don't put values in the namespace
13 13 # that aren't pickleable (module imports are okay, they're removed automatically).
14 14 #
15 15 # All configuration values have a default value; values that are commented out
16 16 # serve to show the default value.
17 17
18 18 import sys, os
19 19
20 20 # If your extensions are in another directory, add it here. If the directory
21 21 # is relative to the documentation root, use os.path.abspath to make it
22 22 # absolute, like shown here.
23 23 sys.path.append(os.path.abspath('../sphinxext'))
24 24
25 25 # Import support for ipython console session syntax highlighting (lives
26 26 # in the sphinxext directory defined above)
27 27 import ipython_console_highlighting
28 28
29 29 # We load the ipython release info into a dict by explicit execution
30 30 iprelease = {}
31 31 execfile('../../IPython/core/release.py',iprelease)
32 32
33 33 # General configuration
34 34 # ---------------------
35 35
36 36 # Add any Sphinx extension module names here, as strings. They can be extensions
37 37 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
38 38 extensions = [
39 39 # 'matplotlib.sphinxext.mathmpl',
40 40 'matplotlib.sphinxext.only_directives',
41 41 # 'matplotlib.sphinxext.plot_directive',
42 42 'sphinx.ext.autodoc',
43 43 'sphinx.ext.doctest',
44 44 'inheritance_diagram',
45 45 'ipython_console_highlighting',
46 46 'numpydoc', # to preprocess docstrings
47 47 ]
48 48
49 49 # Add any paths that contain templates here, relative to this directory.
50 50 templates_path = ['_templates']
51 51
52 52 # The suffix of source filenames.
53 53 source_suffix = '.txt'
54 54
55 55 # The master toctree document.
56 56 master_doc = 'index'
57 57
58 58 # General substitutions.
59 59 project = 'IPython'
60 60 copyright = '2008, The IPython Development Team'
61 61
62 62 # The default replacements for |version| and |release|, also used in various
63 63 # other places throughout the built documents.
64 64 #
65 65 # The full version, including alpha/beta/rc tags.
66 66 release = iprelease['version']
67 67 # The short X.Y version.
68 68 version = '.'.join(release.split('.',2)[:2])
69 69
70 70
71 71 # There are two options for replacing |today|: either, you set today to some
72 72 # non-false value, then it is used:
73 73 #today = ''
74 74 # Else, today_fmt is used as the format for a strftime call.
75 75 today_fmt = '%B %d, %Y'
76 76
77 77 # List of documents that shouldn't be included in the build.
78 78 #unused_docs = []
79 79
80 80 # List of directories, relative to source directories, that shouldn't be searched
81 81 # for source files.
82 82 exclude_dirs = ['attic']
83 83
84 84 # If true, '()' will be appended to :func: etc. cross-reference text.
85 85 #add_function_parentheses = True
86 86
87 87 # If true, the current module name will be prepended to all description
88 88 # unit titles (such as .. function::).
89 89 #add_module_names = True
90 90
91 91 # If true, sectionauthor and moduleauthor directives will be shown in the
92 92 # output. They are ignored by default.
93 93 #show_authors = False
94 94
95 95 # The name of the Pygments (syntax highlighting) style to use.
96 96 pygments_style = 'sphinx'
97 97
98 98
99 99 # Options for HTML output
100 100 # -----------------------
101 101
102 102 # The style sheet to use for HTML and HTML Help pages. A file of that name
103 103 # must exist either in Sphinx' static/ path, or in one of the custom paths
104 104 # given in html_static_path.
105 105 html_style = 'default.css'
106 106
107 107 # The name for this set of Sphinx documents. If None, it defaults to
108 108 # "<project> v<release> documentation".
109 109 #html_title = None
110 110
111 111 # The name of an image file (within the static path) to place at the top of
112 112 # the sidebar.
113 113 #html_logo = None
114 114
115 115 # Add any paths that contain custom static files (such as style sheets) here,
116 116 # relative to this directory. They are copied after the builtin static files,
117 117 # so a file named "default.css" will overwrite the builtin "default.css".
118 118 html_static_path = ['_static']
119 119
120 120 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
121 121 # using the given strftime format.
122 122 html_last_updated_fmt = '%b %d, %Y'
123 123
124 124 # If true, SmartyPants will be used to convert quotes and dashes to
125 125 # typographically correct entities.
126 126 #html_use_smartypants = True
127 127
128 128 # Custom sidebar templates, maps document names to template names.
129 129 #html_sidebars = {}
130 130
131 131 # Additional templates that should be rendered to pages, maps page names to
132 132 # template names.
133 133 #html_additional_pages = {}
134 134
135 135 # If false, no module index is generated.
136 136 #html_use_modindex = True
137 137
138 138 # If true, the reST sources are included in the HTML build as _sources/<name>.
139 139 #html_copy_source = True
140 140
141 141 # If true, an OpenSearch description file will be output, and all pages will
142 142 # contain a <link> tag referring to it. The value of this option must be the
143 143 # base URL from which the finished HTML is served.
144 144 #html_use_opensearch = ''
145 145
146 146 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
147 147 #html_file_suffix = ''
148 148
149 149 # Output file base name for HTML help builder.
150 150 htmlhelp_basename = 'ipythondoc'
151 151
152 152
153 153 # Options for LaTeX output
154 154 # ------------------------
155 155
156 156 # The paper size ('letter' or 'a4').
157 157 latex_paper_size = 'letter'
158 158
159 159 # The font size ('10pt', '11pt' or '12pt').
160 160 latex_font_size = '11pt'
161 161
162 162 # Grouping the document tree into LaTeX files. List of tuples
163 163 # (source start file, target name, title, author, document class [howto/manual]).
164 164
165 165 latex_documents = [ ('index', 'ipython.tex', 'IPython Documentation',
166 166 ur"""The IPython Development Team""",
167 167 'manual', True),
168 168 ]
169 169
170 170 # The name of an image file (relative to this directory) to place at the top of
171 171 # the title page.
172 172 #latex_logo = None
173 173
174 174 # For "manual" documents, if this is true, then toplevel headings are parts,
175 175 # not chapters.
176 176 #latex_use_parts = False
177 177
178 178 # Additional stuff for the LaTeX preamble.
179 179 #latex_preamble = ''
180 180
181 181 # Documents to append as an appendix to all manuals.
182 182 #latex_appendices = []
183 183
184 184 # If false, no module index is generated.
185 #latex_use_modindex = True
185 latex_use_modindex = True
186 186
187 187
188 188 # Cleanup
189 189 # -------
190 190 # delete release info to avoid pickling errors from sphinx
191 191
192 192 del iprelease
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (811 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now