##// END OF EJS Templates
New version of ipcluster and docs updates....
Brian Granger -
r1797:a2c0df6b merge
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (1867 lines changed) Show them Hide them
@@ -0,0 +1,1867 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright οΏ½ 2006 Steven J. Bethard <steven.bethard@gmail.com>.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted under the terms of the 3-clause BSD
7 # license. No warranty expressed or implied.
8 # For details, see the accompanying file LICENSE.txt.
9
10 """Command-line parsing library
11
12 This module is an optparse-inspired command-line parsing library that:
13
14 * handles both optional and positional arguments
15 * produces highly informative usage messages
16 * supports parsers that dispatch to sub-parsers
17
18 The following is a simple usage example that sums integers from the
19 command-line and writes the result to a file:
20
21 parser = argparse.ArgumentParser(
22 description='sum the integers at the command line')
23 parser.add_argument(
24 'integers', metavar='int', nargs='+', type=int,
25 help='an integer to be summed')
26 parser.add_argument(
27 '--log', default=sys.stdout, type=argparse.FileType('w'),
28 help='the file where the sum should be written')
29 args = parser.parse_args()
30 args.log.write('%s' % sum(args.integers))
31 args.log.close()
32
33 The module contains the following public classes:
34
35 ArgumentParser -- The main entry point for command-line parsing. As the
36 example above shows, the add_argument() method is used to populate
37 the parser with actions for optional and positional arguments. Then
38 the parse_args() method is invoked to convert the args at the
39 command-line into an object with attributes.
40
41 ArgumentError -- The exception raised by ArgumentParser objects when
42 there are errors with the parser's actions. Errors raised while
43 parsing the command-line are caught by ArgumentParser and emitted
44 as command-line messages.
45
46 FileType -- A factory for defining types of files to be created. As the
47 example above shows, instances of FileType are typically passed as
48 the type= argument of add_argument() calls.
49
50 Action -- The base class for parser actions. Typically actions are
51 selected by passing strings like 'store_true' or 'append_const' to
52 the action= argument of add_argument(). However, for greater
53 customization of ArgumentParser actions, subclasses of Action may
54 be defined and passed as the action= argument.
55
56 HelpFormatter, RawDescriptionHelpFormatter -- Formatter classes which
57 may be passed as the formatter_class= argument to the
58 ArgumentParser constructor. HelpFormatter is the default, while
59 RawDescriptionHelpFormatter tells the parser not to perform any
60 line-wrapping on description text.
61
62 All other classes in this module are considered implementation details.
63 (Also note that HelpFormatter and RawDescriptionHelpFormatter are only
64 considered public as object names -- the API of the formatter objects is
65 still considered an implementation detail.)
66 """
67
68 __version__ = '0.8.0'
69
70 import os as _os
71 import re as _re
72 import sys as _sys
73 import textwrap as _textwrap
74
75 from gettext import gettext as _
76
77 SUPPRESS = '==SUPPRESS=='
78
79 OPTIONAL = '?'
80 ZERO_OR_MORE = '*'
81 ONE_OR_MORE = '+'
82 PARSER = '==PARSER=='
83
84 # =============================
85 # Utility functions and classes
86 # =============================
87
88 class _AttributeHolder(object):
89 """Abstract base class that provides __repr__.
90
91 The __repr__ method returns a string in the format:
92 ClassName(attr=name, attr=name, ...)
93 The attributes are determined either by a class-level attribute,
94 '_kwarg_names', or by inspecting the instance __dict__.
95 """
96
97 def __repr__(self):
98 type_name = type(self).__name__
99 arg_strings = []
100 for arg in self._get_args():
101 arg_strings.append(repr(arg))
102 for name, value in self._get_kwargs():
103 arg_strings.append('%s=%r' % (name, value))
104 return '%s(%s)' % (type_name, ', '.join(arg_strings))
105
106 def _get_kwargs(self):
107 return sorted(self.__dict__.items())
108
109 def _get_args(self):
110 return []
111
112 def _ensure_value(namespace, name, value):
113 if getattr(namespace, name, None) is None:
114 setattr(namespace, name, value)
115 return getattr(namespace, name)
116
117
118
119 # ===============
120 # Formatting Help
121 # ===============
122
123 class HelpFormatter(object):
124
125 def __init__(self,
126 prog,
127 indent_increment=2,
128 max_help_position=24,
129 width=None):
130
131 # default setting for width
132 if width is None:
133 try:
134 width = int(_os.environ['COLUMNS'])
135 except (KeyError, ValueError):
136 width = 80
137 width -= 2
138
139 self._prog = prog
140 self._indent_increment = indent_increment
141 self._max_help_position = max_help_position
142 self._width = width
143
144 self._current_indent = 0
145 self._level = 0
146 self._action_max_length = 0
147
148 self._root_section = self._Section(self, None)
149 self._current_section = self._root_section
150
151 self._whitespace_matcher = _re.compile(r'\s+')
152 self._long_break_matcher = _re.compile(r'\n\n\n+')
153
154 # ===============================
155 # Section and indentation methods
156 # ===============================
157
158 def _indent(self):
159 self._current_indent += self._indent_increment
160 self._level += 1
161
162 def _dedent(self):
163 self._current_indent -= self._indent_increment
164 assert self._current_indent >= 0, 'Indent decreased below 0.'
165 self._level -= 1
166
167 class _Section(object):
168 def __init__(self, formatter, parent, heading=None):
169 self.formatter = formatter
170 self.parent = parent
171 self.heading = heading
172 self.items = []
173
174 def format_help(self):
175 # format the indented section
176 if self.parent is not None:
177 self.formatter._indent()
178 join = self.formatter._join_parts
179 for func, args in self.items:
180 func(*args)
181 item_help = join(func(*args) for func, args in self.items)
182 if self.parent is not None:
183 self.formatter._dedent()
184
185 # return nothing if the section was empty
186 if not item_help:
187 return ''
188
189 # add the heading if the section was non-empty
190 if self.heading is not SUPPRESS and self.heading is not None:
191 current_indent = self.formatter._current_indent
192 heading = '%*s%s:\n' % (current_indent, '', self.heading)
193 else:
194 heading = ''
195
196 # join the section-initial newline, the heading and the help
197 return join(['\n', heading, item_help, '\n'])
198
199 def _add_item(self, func, args):
200 self._current_section.items.append((func, args))
201
202 # ========================
203 # Message building methods
204 # ========================
205
206 def start_section(self, heading):
207 self._indent()
208 section = self._Section(self, self._current_section, heading)
209 self._add_item(section.format_help, [])
210 self._current_section = section
211
212 def end_section(self):
213 self._current_section = self._current_section.parent
214 self._dedent()
215
216 def add_text(self, text):
217 if text is not SUPPRESS and text is not None:
218 self._add_item(self._format_text, [text])
219
220 def add_usage(self, usage, optionals, positionals, prefix=None):
221 if usage is not SUPPRESS:
222 args = usage, optionals, positionals, prefix
223 self._add_item(self._format_usage, args)
224
225 def add_argument(self, action):
226 if action.help is not SUPPRESS:
227
228 # find all invocations
229 get_invocation = self._format_action_invocation
230 invocations = [get_invocation(action)]
231 for subaction in self._iter_indented_subactions(action):
232 invocations.append(get_invocation(subaction))
233
234 # update the maximum item length
235 invocation_length = max(len(s) for s in invocations)
236 action_length = invocation_length + self._current_indent
237 self._action_max_length = max(self._action_max_length,
238 action_length)
239
240 # add the item to the list
241 self._add_item(self._format_action, [action])
242
243 def add_arguments(self, actions):
244 for action in actions:
245 self.add_argument(action)
246
247 # =======================
248 # Help-formatting methods
249 # =======================
250
251 def format_help(self):
252 help = self._root_section.format_help() % dict(prog=self._prog)
253 if help:
254 help = self._long_break_matcher.sub('\n\n', help)
255 help = help.strip('\n') + '\n'
256 return help
257
258 def _join_parts(self, part_strings):
259 return ''.join(part
260 for part in part_strings
261 if part and part is not SUPPRESS)
262
263 def _format_usage(self, usage, optionals, positionals, prefix):
264 if prefix is None:
265 prefix = _('usage: ')
266
267 # if no optionals or positionals are available, usage is just prog
268 if usage is None and not optionals and not positionals:
269 usage = '%(prog)s'
270
271 # if optionals and positionals are available, calculate usage
272 elif usage is None:
273 usage = '%(prog)s' % dict(prog=self._prog)
274
275 # determine width of "usage: PROG" and width of text
276 prefix_width = len(prefix) + len(usage) + 1
277 prefix_indent = self._current_indent + prefix_width
278 text_width = self._width - self._current_indent
279
280 # put them on one line if they're short enough
281 format = self._format_actions_usage
282 action_usage = format(optionals + positionals)
283 if prefix_width + len(action_usage) + 1 < text_width:
284 usage = '%s %s' % (usage, action_usage)
285
286 # if they're long, wrap optionals and positionals individually
287 else:
288 optional_usage = format(optionals)
289 positional_usage = format(positionals)
290 indent = ' ' * prefix_indent
291
292 # usage is made of PROG, optionals and positionals
293 parts = [usage, ' ']
294
295 # options always get added right after PROG
296 if optional_usage:
297 parts.append(_textwrap.fill(
298 optional_usage, text_width,
299 initial_indent=indent,
300 subsequent_indent=indent).lstrip())
301
302 # if there were options, put arguments on the next line
303 # otherwise, start them right after PROG
304 if positional_usage:
305 part = _textwrap.fill(
306 positional_usage, text_width,
307 initial_indent=indent,
308 subsequent_indent=indent).lstrip()
309 if optional_usage:
310 part = '\n' + indent + part
311 parts.append(part)
312 usage = ''.join(parts)
313
314 # prefix with 'usage:'
315 return '%s%s\n\n' % (prefix, usage)
316
317 def _format_actions_usage(self, actions):
318 parts = []
319 for action in actions:
320 if action.help is SUPPRESS:
321 continue
322
323 # produce all arg strings
324 if not action.option_strings:
325 parts.append(self._format_args(action, action.dest))
326
327 # produce the first way to invoke the option in brackets
328 else:
329 option_string = action.option_strings[0]
330
331 # if the Optional doesn't take a value, format is:
332 # -s or --long
333 if action.nargs == 0:
334 part = '%s' % option_string
335
336 # if the Optional takes a value, format is:
337 # -s ARGS or --long ARGS
338 else:
339 default = action.dest.upper()
340 args_string = self._format_args(action, default)
341 part = '%s %s' % (option_string, args_string)
342
343 # make it look optional if it's not required
344 if not action.required:
345 part = '[%s]' % part
346 parts.append(part)
347
348 return ' '.join(parts)
349
350 def _format_text(self, text):
351 text_width = self._width - self._current_indent
352 indent = ' ' * self._current_indent
353 return self._fill_text(text, text_width, indent) + '\n\n'
354
355 def _format_action(self, action):
356 # determine the required width and the entry label
357 help_position = min(self._action_max_length + 2,
358 self._max_help_position)
359 help_width = self._width - help_position
360 action_width = help_position - self._current_indent - 2
361 action_header = self._format_action_invocation(action)
362
363 # ho nelp; start on same line and add a final newline
364 if not action.help:
365 tup = self._current_indent, '', action_header
366 action_header = '%*s%s\n' % tup
367
368 # short action name; start on the same line and pad two spaces
369 elif len(action_header) <= action_width:
370 tup = self._current_indent, '', action_width, action_header
371 action_header = '%*s%-*s ' % tup
372 indent_first = 0
373
374 # long action name; start on the next line
375 else:
376 tup = self._current_indent, '', action_header
377 action_header = '%*s%s\n' % tup
378 indent_first = help_position
379
380 # collect the pieces of the action help
381 parts = [action_header]
382
383 # if there was help for the action, add lines of help text
384 if action.help:
385 help_text = self._expand_help(action)
386 help_lines = self._split_lines(help_text, help_width)
387 parts.append('%*s%s\n' % (indent_first, '', help_lines[0]))
388 for line in help_lines[1:]:
389 parts.append('%*s%s\n' % (help_position, '', line))
390
391 # or add a newline if the description doesn't end with one
392 elif not action_header.endswith('\n'):
393 parts.append('\n')
394
395 # if there are any sub-actions, add their help as well
396 for subaction in self._iter_indented_subactions(action):
397 parts.append(self._format_action(subaction))
398
399 # return a single string
400 return self._join_parts(parts)
401
402 def _format_action_invocation(self, action):
403 if not action.option_strings:
404 return self._format_metavar(action, action.dest)
405
406 else:
407 parts = []
408
409 # if the Optional doesn't take a value, format is:
410 # -s, --long
411 if action.nargs == 0:
412 parts.extend(action.option_strings)
413
414 # if the Optional takes a value, format is:
415 # -s ARGS, --long ARGS
416 else:
417 default = action.dest.upper()
418 args_string = self._format_args(action, default)
419 for option_string in action.option_strings:
420 parts.append('%s %s' % (option_string, args_string))
421
422 return ', '.join(parts)
423
424 def _format_metavar(self, action, default_metavar):
425 if action.metavar is not None:
426 name = action.metavar
427 elif action.choices is not None:
428 choice_strs = (str(choice) for choice in action.choices)
429 name = '{%s}' % ','.join(choice_strs)
430 else:
431 name = default_metavar
432 return name
433
434 def _format_args(self, action, default_metavar):
435 name = self._format_metavar(action, default_metavar)
436 if action.nargs is None:
437 result = name
438 elif action.nargs == OPTIONAL:
439 result = '[%s]' % name
440 elif action.nargs == ZERO_OR_MORE:
441 result = '[%s [%s ...]]' % (name, name)
442 elif action.nargs == ONE_OR_MORE:
443 result = '%s [%s ...]' % (name, name)
444 elif action.nargs is PARSER:
445 result = '%s ...' % name
446 else:
447 result = ' '.join([name] * action.nargs)
448 return result
449
450 def _expand_help(self, action):
451 params = dict(vars(action), prog=self._prog)
452 for name, value in params.items():
453 if value is SUPPRESS:
454 del params[name]
455 if params.get('choices') is not None:
456 choices_str = ', '.join(str(c) for c in params['choices'])
457 params['choices'] = choices_str
458 return action.help % params
459
460 def _iter_indented_subactions(self, action):
461 try:
462 get_subactions = action._get_subactions
463 except AttributeError:
464 pass
465 else:
466 self._indent()
467 for subaction in get_subactions():
468 yield subaction
469 self._dedent()
470
471 def _split_lines(self, text, width):
472 text = self._whitespace_matcher.sub(' ', text).strip()
473 return _textwrap.wrap(text, width)
474
475 def _fill_text(self, text, width, indent):
476 text = self._whitespace_matcher.sub(' ', text).strip()
477 return _textwrap.fill(text, width, initial_indent=indent,
478 subsequent_indent=indent)
479
480 class RawDescriptionHelpFormatter(HelpFormatter):
481
482 def _fill_text(self, text, width, indent):
483 return ''.join(indent + line for line in text.splitlines(True))
484
485 class RawTextHelpFormatter(RawDescriptionHelpFormatter):
486
487 def _split_lines(self, text, width):
488 return text.splitlines()
489
490 # =====================
491 # Options and Arguments
492 # =====================
493
494 class ArgumentError(Exception):
495 """ArgumentError(message, argument)
496
497 Raised whenever there was an error creating or using an argument
498 (optional or positional).
499
500 The string value of this exception is the message, augmented with
501 information about the argument that caused it.
502 """
503
504 def __init__(self, argument, message):
505 if argument.option_strings:
506 self.argument_name = '/'.join(argument.option_strings)
507 elif argument.metavar not in (None, SUPPRESS):
508 self.argument_name = argument.metavar
509 elif argument.dest not in (None, SUPPRESS):
510 self.argument_name = argument.dest
511 else:
512 self.argument_name = None
513 self.message = message
514
515 def __str__(self):
516 if self.argument_name is None:
517 format = '%(message)s'
518 else:
519 format = 'argument %(argument_name)s: %(message)s'
520 return format % dict(message=self.message,
521 argument_name=self.argument_name)
522
523 # ==============
524 # Action classes
525 # ==============
526
527 class Action(_AttributeHolder):
528 """Action(*strings, **options)
529
530 Action objects hold the information necessary to convert a
531 set of command-line arguments (possibly including an initial option
532 string) into the desired Python object(s).
533
534 Keyword Arguments:
535
536 option_strings -- A list of command-line option strings which
537 should be associated with this action.
538
539 dest -- The name of the attribute to hold the created object(s)
540
541 nargs -- The number of command-line arguments that should be consumed.
542 By default, one argument will be consumed and a single value will
543 be produced. Other values include:
544 * N (an integer) consumes N arguments (and produces a list)
545 * '?' consumes zero or one arguments
546 * '*' consumes zero or more arguments (and produces a list)
547 * '+' consumes one or more arguments (and produces a list)
548 Note that the difference between the default and nargs=1 is that
549 with the default, a single value will be produced, while with
550 nargs=1, a list containing a single value will be produced.
551
552 const -- The value to be produced if the option is specified and the
553 option uses an action that takes no values.
554
555 default -- The value to be produced if the option is not specified.
556
557 type -- The type which the command-line arguments should be converted
558 to, should be one of 'string', 'int', 'float', 'complex' or a
559 callable object that accepts a single string argument. If None,
560 'string' is assumed.
561
562 choices -- A container of values that should be allowed. If not None,
563 after a command-line argument has been converted to the appropriate
564 type, an exception will be raised if it is not a member of this
565 collection.
566
567 required -- True if the action must always be specified at the command
568 line. This is only meaningful for optional command-line arguments.
569
570 help -- The help string describing the argument.
571
572 metavar -- The name to be used for the option's argument with the help
573 string. If None, the 'dest' value will be used as the name.
574 """
575
576
577 def __init__(self,
578 option_strings,
579 dest,
580 nargs=None,
581 const=None,
582 default=None,
583 type=None,
584 choices=None,
585 required=False,
586 help=None,
587 metavar=None):
588 self.option_strings = option_strings
589 self.dest = dest
590 self.nargs = nargs
591 self.const = const
592 self.default = default
593 self.type = type
594 self.choices = choices
595 self.required = required
596 self.help = help
597 self.metavar = metavar
598
599 def _get_kwargs(self):
600 names = [
601 'option_strings',
602 'dest',
603 'nargs',
604 'const',
605 'default',
606 'type',
607 'choices',
608 'help',
609 'metavar'
610 ]
611 return [(name, getattr(self, name)) for name in names]
612
613 def __call__(self, parser, namespace, values, option_string=None):
614 raise NotImplementedError(_('.__call__() not defined'))
615
616 class _StoreAction(Action):
617 def __init__(self,
618 option_strings,
619 dest,
620 nargs=None,
621 const=None,
622 default=None,
623 type=None,
624 choices=None,
625 required=False,
626 help=None,
627 metavar=None):
628 if nargs == 0:
629 raise ValueError('nargs must be > 0')
630 if const is not None and nargs != OPTIONAL:
631 raise ValueError('nargs must be %r to supply const' % OPTIONAL)
632 super(_StoreAction, self).__init__(
633 option_strings=option_strings,
634 dest=dest,
635 nargs=nargs,
636 const=const,
637 default=default,
638 type=type,
639 choices=choices,
640 required=required,
641 help=help,
642 metavar=metavar)
643
644 def __call__(self, parser, namespace, values, option_string=None):
645 setattr(namespace, self.dest, values)
646
647 class _StoreConstAction(Action):
648 def __init__(self,
649 option_strings,
650 dest,
651 const,
652 default=None,
653 required=False,
654 help=None,
655 metavar=None):
656 super(_StoreConstAction, self).__init__(
657 option_strings=option_strings,
658 dest=dest,
659 nargs=0,
660 const=const,
661 default=default,
662 required=required,
663 help=help)
664
665 def __call__(self, parser, namespace, values, option_string=None):
666 setattr(namespace, self.dest, self.const)
667
668 class _StoreTrueAction(_StoreConstAction):
669 def __init__(self,
670 option_strings,
671 dest,
672 default=False,
673 required=False,
674 help=None):
675 super(_StoreTrueAction, self).__init__(
676 option_strings=option_strings,
677 dest=dest,
678 const=True,
679 default=default,
680 required=required,
681 help=help)
682
683 class _StoreFalseAction(_StoreConstAction):
684 def __init__(self,
685 option_strings,
686 dest,
687 default=True,
688 required=False,
689 help=None):
690 super(_StoreFalseAction, self).__init__(
691 option_strings=option_strings,
692 dest=dest,
693 const=False,
694 default=default,
695 required=required,
696 help=help)
697
698 class _AppendAction(Action):
699 def __init__(self,
700 option_strings,
701 dest,
702 nargs=None,
703 const=None,
704 default=None,
705 type=None,
706 choices=None,
707 required=False,
708 help=None,
709 metavar=None):
710 if nargs == 0:
711 raise ValueError('nargs must be > 0')
712 if const is not None and nargs != OPTIONAL:
713 raise ValueError('nargs must be %r to supply const' % OPTIONAL)
714 super(_AppendAction, self).__init__(
715 option_strings=option_strings,
716 dest=dest,
717 nargs=nargs,
718 const=const,
719 default=default,
720 type=type,
721 choices=choices,
722 required=required,
723 help=help,
724 metavar=metavar)
725
726 def __call__(self, parser, namespace, values, option_string=None):
727 _ensure_value(namespace, self.dest, []).append(values)
728
729 class _AppendConstAction(Action):
730 def __init__(self,
731 option_strings,
732 dest,
733 const,
734 default=None,
735 required=False,
736 help=None,
737 metavar=None):
738 super(_AppendConstAction, self).__init__(
739 option_strings=option_strings,
740 dest=dest,
741 nargs=0,
742 const=const,
743 default=default,
744 required=required,
745 help=help,
746 metavar=metavar)
747
748 def __call__(self, parser, namespace, values, option_string=None):
749 _ensure_value(namespace, self.dest, []).append(self.const)
750
751 class _CountAction(Action):
752 def __init__(self,
753 option_strings,
754 dest,
755 default=None,
756 required=False,
757 help=None):
758 super(_CountAction, self).__init__(
759 option_strings=option_strings,
760 dest=dest,
761 nargs=0,
762 default=default,
763 required=required,
764 help=help)
765
766 def __call__(self, parser, namespace, values, option_string=None):
767 new_count = _ensure_value(namespace, self.dest, 0) + 1
768 setattr(namespace, self.dest, new_count)
769
770 class _HelpAction(Action):
771 def __init__(self,
772 option_strings,
773 dest=SUPPRESS,
774 default=SUPPRESS,
775 help=None):
776 super(_HelpAction, self).__init__(
777 option_strings=option_strings,
778 dest=dest,
779 default=default,
780 nargs=0,
781 help=help)
782
783 def __call__(self, parser, namespace, values, option_string=None):
784 parser.print_help()
785 parser.exit()
786
787 class _VersionAction(Action):
788 def __init__(self,
789 option_strings,
790 dest=SUPPRESS,
791 default=SUPPRESS,
792 help=None):
793 super(_VersionAction, self).__init__(
794 option_strings=option_strings,
795 dest=dest,
796 default=default,
797 nargs=0,
798 help=help)
799
800 def __call__(self, parser, namespace, values, option_string=None):
801 parser.print_version()
802 parser.exit()
803
804 class _SubParsersAction(Action):
805
806 class _ChoicesPseudoAction(Action):
807 def __init__(self, name, help):
808 sup = super(_SubParsersAction._ChoicesPseudoAction, self)
809 sup.__init__(option_strings=[], dest=name, help=help)
810
811
812 def __init__(self,
813 option_strings,
814 prog,
815 parser_class,
816 dest=SUPPRESS,
817 help=None,
818 metavar=None):
819
820 self._prog_prefix = prog
821 self._parser_class = parser_class
822 self._name_parser_map = {}
823 self._choices_actions = []
824
825 super(_SubParsersAction, self).__init__(
826 option_strings=option_strings,
827 dest=dest,
828 nargs=PARSER,
829 choices=self._name_parser_map,
830 help=help,
831 metavar=metavar)
832
833 def add_parser(self, name, **kwargs):
834 # set prog from the existing prefix
835 if kwargs.get('prog') is None:
836 kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
837
838 # create a pseudo-action to hold the choice help
839 if 'help' in kwargs:
840 help = kwargs.pop('help')
841 choice_action = self._ChoicesPseudoAction(name, help)
842 self._choices_actions.append(choice_action)
843
844 # create the parser and add it to the map
845 parser = self._parser_class(**kwargs)
846 self._name_parser_map[name] = parser
847 return parser
848
849 def _get_subactions(self):
850 return self._choices_actions
851
852 def __call__(self, parser, namespace, values, option_string=None):
853 parser_name = values[0]
854 arg_strings = values[1:]
855
856 # set the parser name if requested
857 if self.dest is not SUPPRESS:
858 setattr(namespace, self.dest, parser_name)
859
860 # select the parser
861 try:
862 parser = self._name_parser_map[parser_name]
863 except KeyError:
864 tup = parser_name, ', '.join(self._name_parser_map)
865 msg = _('unknown parser %r (choices: %s)' % tup)
866 raise ArgumentError(self, msg)
867
868 # parse all the remaining options into the namespace
869 parser.parse_args(arg_strings, namespace)
870
871
872 # ==============
873 # Type classes
874 # ==============
875
876 class FileType(object):
877 """Factory for creating file object types
878
879 Instances of FileType are typically passed as type= arguments to the
880 ArgumentParser add_argument() method.
881
882 Keyword Arguments:
883 mode -- A string indicating how the file is to be opened. Accepts the
884 same values as the builtin open() function.
885 bufsize -- The file's desired buffer size. Accepts the same values as
886 the builtin open() function.
887 """
888 def __init__(self, mode='r', bufsize=None):
889 self._mode = mode
890 self._bufsize = bufsize
891
892 def __call__(self, string):
893 # the special argument "-" means sys.std{in,out}
894 if string == '-':
895 if self._mode == 'r':
896 return _sys.stdin
897 elif self._mode == 'w':
898 return _sys.stdout
899 else:
900 msg = _('argument "-" with mode %r' % self._mode)
901 raise ValueError(msg)
902
903 # all other arguments are used as file names
904 if self._bufsize:
905 return open(string, self._mode, self._bufsize)
906 else:
907 return open(string, self._mode)
908
909
910 # ===========================
911 # Optional and Positional Parsing
912 # ===========================
913
914 class Namespace(_AttributeHolder):
915
916 def __init__(self, **kwargs):
917 for name, value in kwargs.iteritems():
918 setattr(self, name, value)
919
920 def __eq__(self, other):
921 return vars(self) == vars(other)
922
923 def __ne__(self, other):
924 return not (self == other)
925
926
927 class _ActionsContainer(object):
928 def __init__(self,
929 description,
930 prefix_chars,
931 argument_default,
932 conflict_handler):
933 super(_ActionsContainer, self).__init__()
934
935 self.description = description
936 self.argument_default = argument_default
937 self.prefix_chars = prefix_chars
938 self.conflict_handler = conflict_handler
939
940 # set up registries
941 self._registries = {}
942
943 # register actions
944 self.register('action', None, _StoreAction)
945 self.register('action', 'store', _StoreAction)
946 self.register('action', 'store_const', _StoreConstAction)
947 self.register('action', 'store_true', _StoreTrueAction)
948 self.register('action', 'store_false', _StoreFalseAction)
949 self.register('action', 'append', _AppendAction)
950 self.register('action', 'append_const', _AppendConstAction)
951 self.register('action', 'count', _CountAction)
952 self.register('action', 'help', _HelpAction)
953 self.register('action', 'version', _VersionAction)
954 self.register('action', 'parsers', _SubParsersAction)
955
956 # raise an exception if the conflict handler is invalid
957 self._get_handler()
958
959 # action storage
960 self._optional_actions_list = []
961 self._positional_actions_list = []
962 self._positional_actions_full_list = []
963 self._option_strings = {}
964
965 # defaults storage
966 self._defaults = {}
967
968 # ====================
969 # Registration methods
970 # ====================
971
972 def register(self, registry_name, value, object):
973 registry = self._registries.setdefault(registry_name, {})
974 registry[value] = object
975
976 def _registry_get(self, registry_name, value, default=None):
977 return self._registries[registry_name].get(value, default)
978
979 # ==================================
980 # Namespace default settings methods
981 # ==================================
982
983 def set_defaults(self, **kwargs):
984 self._defaults.update(kwargs)
985
986 # if these defaults match any existing arguments, replace
987 # the previous default on the object with the new one
988 for action_list in [self._option_strings.values(),
989 self._positional_actions_full_list]:
990 for action in action_list:
991 if action.dest in kwargs:
992 action.default = kwargs[action.dest]
993
994 # =======================
995 # Adding argument actions
996 # =======================
997
998 def add_argument(self, *args, **kwargs):
999 """
1000 add_argument(dest, ..., name=value, ...)
1001 add_argument(option_string, option_string, ..., name=value, ...)
1002 """
1003
1004 # if no positional args are supplied or only one is supplied and
1005 # it doesn't look like an option string, parse a positional
1006 # argument
1007 chars = self.prefix_chars
1008 if not args or len(args) == 1 and args[0][0] not in chars:
1009 kwargs = self._get_positional_kwargs(*args, **kwargs)
1010
1011 # otherwise, we're adding an optional argument
1012 else:
1013 kwargs = self._get_optional_kwargs(*args, **kwargs)
1014
1015 # if no default was supplied, use the parser-level default
1016 if 'default' not in kwargs:
1017 dest = kwargs['dest']
1018 if dest in self._defaults:
1019 kwargs['default'] = self._defaults[dest]
1020 elif self.argument_default is not None:
1021 kwargs['default'] = self.argument_default
1022
1023 # create the action object, and add it to the parser
1024 action_class = self._pop_action_class(kwargs)
1025 action = action_class(**kwargs)
1026 return self._add_action(action)
1027
1028 def _add_action(self, action):
1029 # resolve any conflicts
1030 self._check_conflict(action)
1031
1032 # add to optional or positional list
1033 if action.option_strings:
1034 self._optional_actions_list.append(action)
1035 else:
1036 self._positional_actions_list.append(action)
1037 self._positional_actions_full_list.append(action)
1038 action.container = self
1039
1040 # index the action by any option strings it has
1041 for option_string in action.option_strings:
1042 self._option_strings[option_string] = action
1043
1044 # return the created action
1045 return action
1046
1047 def _add_container_actions(self, container):
1048 for action in container._optional_actions_list:
1049 self._add_action(action)
1050 for action in container._positional_actions_list:
1051 self._add_action(action)
1052
1053 def _get_positional_kwargs(self, dest, **kwargs):
1054 # make sure required is not specified
1055 if 'required' in kwargs:
1056 msg = _("'required' is an invalid argument for positionals")
1057 raise TypeError(msg)
1058
1059 # return the keyword arguments with no option strings
1060 return dict(kwargs, dest=dest, option_strings=[])
1061
1062 def _get_optional_kwargs(self, *args, **kwargs):
1063 # determine short and long option strings
1064 option_strings = []
1065 long_option_strings = []
1066 for option_string in args:
1067 # error on one-or-fewer-character option strings
1068 if len(option_string) < 2:
1069 msg = _('invalid option string %r: '
1070 'must be at least two characters long')
1071 raise ValueError(msg % option_string)
1072
1073 # error on strings that don't start with an appropriate prefix
1074 if not option_string[0] in self.prefix_chars:
1075 msg = _('invalid option string %r: '
1076 'must start with a character %r')
1077 tup = option_string, self.prefix_chars
1078 raise ValueError(msg % tup)
1079
1080 # error on strings that are all prefix characters
1081 if not (set(option_string) - set(self.prefix_chars)):
1082 msg = _('invalid option string %r: '
1083 'must contain characters other than %r')
1084 tup = option_string, self.prefix_chars
1085 raise ValueError(msg % tup)
1086
1087 # strings starting with two prefix characters are long options
1088 option_strings.append(option_string)
1089 if option_string[0] in self.prefix_chars:
1090 if option_string[1] in self.prefix_chars:
1091 long_option_strings.append(option_string)
1092
1093 # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
1094 dest = kwargs.pop('dest', None)
1095 if dest is None:
1096 if long_option_strings:
1097 dest_option_string = long_option_strings[0]
1098 else:
1099 dest_option_string = option_strings[0]
1100 dest = dest_option_string.lstrip(self.prefix_chars)
1101 dest = dest.replace('-', '_')
1102
1103 # return the updated keyword arguments
1104 return dict(kwargs, dest=dest, option_strings=option_strings)
1105
1106 def _pop_action_class(self, kwargs, default=None):
1107 action = kwargs.pop('action', default)
1108 return self._registry_get('action', action, action)
1109
1110 def _get_handler(self):
1111 # determine function from conflict handler string
1112 handler_func_name = '_handle_conflict_%s' % self.conflict_handler
1113 try:
1114 return getattr(self, handler_func_name)
1115 except AttributeError:
1116 msg = _('invalid conflict_resolution value: %r')
1117 raise ValueError(msg % self.conflict_handler)
1118
1119 def _check_conflict(self, action):
1120
1121 # find all options that conflict with this option
1122 confl_optionals = []
1123 for option_string in action.option_strings:
1124 if option_string in self._option_strings:
1125 confl_optional = self._option_strings[option_string]
1126 confl_optionals.append((option_string, confl_optional))
1127
1128 # resolve any conflicts
1129 if confl_optionals:
1130 conflict_handler = self._get_handler()
1131 conflict_handler(action, confl_optionals)
1132
1133 def _handle_conflict_error(self, action, conflicting_actions):
1134 message = _('conflicting option string(s): %s')
1135 conflict_string = ', '.join(option_string
1136 for option_string, action
1137 in conflicting_actions)
1138 raise ArgumentError(action, message % conflict_string)
1139
1140 def _handle_conflict_resolve(self, action, conflicting_actions):
1141
1142 # remove all conflicting options
1143 for option_string, action in conflicting_actions:
1144
1145 # remove the conflicting option
1146 action.option_strings.remove(option_string)
1147 self._option_strings.pop(option_string, None)
1148
1149 # if the option now has no option string, remove it from the
1150 # container holding it
1151 if not action.option_strings:
1152 action.container._optional_actions_list.remove(action)
1153
1154
1155 class _ArgumentGroup(_ActionsContainer):
1156
1157 def __init__(self, container, title=None, description=None, **kwargs):
1158 # add any missing keyword arguments by checking the container
1159 update = kwargs.setdefault
1160 update('conflict_handler', container.conflict_handler)
1161 update('prefix_chars', container.prefix_chars)
1162 update('argument_default', container.argument_default)
1163 super_init = super(_ArgumentGroup, self).__init__
1164 super_init(description=description, **kwargs)
1165
1166 self.title = title
1167 self._registries = container._registries
1168 self._positional_actions_full_list = container._positional_actions_full_list
1169 self._option_strings = container._option_strings
1170 self._defaults = container._defaults
1171
1172
1173 class ArgumentParser(_AttributeHolder, _ActionsContainer):
1174
1175 def __init__(self,
1176 prog=None,
1177 usage=None,
1178 description=None,
1179 epilog=None,
1180 version=None,
1181 parents=[],
1182 formatter_class=HelpFormatter,
1183 prefix_chars='-',
1184 argument_default=None,
1185 conflict_handler='error',
1186 add_help=True):
1187
1188 superinit = super(ArgumentParser, self).__init__
1189 superinit(description=description,
1190 prefix_chars=prefix_chars,
1191 argument_default=argument_default,
1192 conflict_handler=conflict_handler)
1193
1194 # default setting for prog
1195 if prog is None:
1196 prog = _os.path.basename(_sys.argv[0])
1197
1198 self.prog = prog
1199 self.usage = usage
1200 self.epilog = epilog
1201 self.version = version
1202 self.formatter_class = formatter_class
1203 self.add_help = add_help
1204
1205 self._argument_group_class = _ArgumentGroup
1206 self._has_subparsers = False
1207 self._argument_groups = []
1208
1209 # register types
1210 def identity(string):
1211 return string
1212 self.register('type', None, identity)
1213
1214 # add help and version arguments if necessary
1215 # (using explicit default to override global argument_default)
1216 if self.add_help:
1217 self.add_argument(
1218 '-h', '--help', action='help', default=SUPPRESS,
1219 help=_('show this help message and exit'))
1220 if self.version:
1221 self.add_argument(
1222 '-v', '--version', action='version', default=SUPPRESS,
1223 help=_("show program's version number and exit"))
1224
1225 # add parent arguments and defaults
1226 for parent in parents:
1227 self._add_container_actions(parent)
1228 try:
1229 defaults = parent._defaults
1230 except AttributeError:
1231 pass
1232 else:
1233 self._defaults.update(defaults)
1234
1235 # determines whether an "option" looks like a negative number
1236 self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$')
1237
1238
1239 # =======================
1240 # Pretty __repr__ methods
1241 # =======================
1242
1243 def _get_kwargs(self):
1244 names = [
1245 'prog',
1246 'usage',
1247 'description',
1248 'version',
1249 'formatter_class',
1250 'conflict_handler',
1251 'add_help',
1252 ]
1253 return [(name, getattr(self, name)) for name in names]
1254
1255 # ==================================
1256 # Optional/Positional adding methods
1257 # ==================================
1258
1259 def add_argument_group(self, *args, **kwargs):
1260 group = self._argument_group_class(self, *args, **kwargs)
1261 self._argument_groups.append(group)
1262 return group
1263
1264 def add_subparsers(self, **kwargs):
1265 if self._has_subparsers:
1266 self.error(_('cannot have multiple subparser arguments'))
1267
1268 # add the parser class to the arguments if it's not present
1269 kwargs.setdefault('parser_class', type(self))
1270
1271 # prog defaults to the usage message of this parser, skipping
1272 # optional arguments and with no "usage:" prefix
1273 if kwargs.get('prog') is None:
1274 formatter = self._get_formatter()
1275 formatter.add_usage(self.usage, [],
1276 self._get_positional_actions(), '')
1277 kwargs['prog'] = formatter.format_help().strip()
1278
1279 # create the parsers action and add it to the positionals list
1280 parsers_class = self._pop_action_class(kwargs, 'parsers')
1281 action = parsers_class(option_strings=[], **kwargs)
1282 self._positional_actions_list.append(action)
1283 self._positional_actions_full_list.append(action)
1284 self._has_subparsers = True
1285
1286 # return the created parsers action
1287 return action
1288
1289 def _add_container_actions(self, container):
1290 super(ArgumentParser, self)._add_container_actions(container)
1291 try:
1292 groups = container._argument_groups
1293 except AttributeError:
1294 pass
1295 else:
1296 for group in groups:
1297 new_group = self.add_argument_group(
1298 title=group.title,
1299 description=group.description,
1300 conflict_handler=group.conflict_handler)
1301 new_group._add_container_actions(group)
1302
1303 def _get_optional_actions(self):
1304 actions = []
1305 actions.extend(self._optional_actions_list)
1306 for argument_group in self._argument_groups:
1307 actions.extend(argument_group._optional_actions_list)
1308 return actions
1309
1310 def _get_positional_actions(self):
1311 return list(self._positional_actions_full_list)
1312
1313
1314 # =====================================
1315 # Command line argument parsing methods
1316 # =====================================
1317
1318 def parse_args(self, args=None, namespace=None):
1319 # args default to the system args
1320 if args is None:
1321 args = _sys.argv[1:]
1322
1323 # default Namespace built from parser defaults
1324 if namespace is None:
1325 namespace = Namespace()
1326
1327 # add any action defaults that aren't present
1328 optional_actions = self._get_optional_actions()
1329 positional_actions = self._get_positional_actions()
1330 for action in optional_actions + positional_actions:
1331 if action.dest is not SUPPRESS:
1332 if not hasattr(namespace, action.dest):
1333 if action.default is not SUPPRESS:
1334 default = action.default
1335 if isinstance(action.default, basestring):
1336 default = self._get_value(action, default)
1337 setattr(namespace, action.dest, default)
1338
1339 # add any parser defaults that aren't present
1340 for dest, value in self._defaults.iteritems():
1341 if not hasattr(namespace, dest):
1342 setattr(namespace, dest, value)
1343
1344 # parse the arguments and exit if there are any errors
1345 try:
1346 result = self._parse_args(args, namespace)
1347 except ArgumentError, err:
1348 self.error(str(err))
1349
1350 # make sure all required optionals are present
1351 for action in self._get_optional_actions():
1352 if action.required:
1353 if getattr(result, action.dest, None) is None:
1354 opt_strs = '/'.join(action.option_strings)
1355 msg = _('option %s is required' % opt_strs)
1356 self.error(msg)
1357
1358 # return the parsed arguments
1359 return result
1360
1361 def _parse_args(self, arg_strings, namespace):
1362
1363 # find all option indices, and determine the arg_string_pattern
1364 # which has an 'O' if there is an option at an index,
1365 # an 'A' if there is an argument, or a '-' if there is a '--'
1366 option_string_indices = {}
1367 arg_string_pattern_parts = []
1368 arg_strings_iter = iter(arg_strings)
1369 for i, arg_string in enumerate(arg_strings_iter):
1370
1371 # all args after -- are non-options
1372 if arg_string == '--':
1373 arg_string_pattern_parts.append('-')
1374 for arg_string in arg_strings_iter:
1375 arg_string_pattern_parts.append('A')
1376
1377 # otherwise, add the arg to the arg strings
1378 # and note the index if it was an option
1379 else:
1380 option_tuple = self._parse_optional(arg_string)
1381 if option_tuple is None:
1382 pattern = 'A'
1383 else:
1384 option_string_indices[i] = option_tuple
1385 pattern = 'O'
1386 arg_string_pattern_parts.append(pattern)
1387
1388 # join the pieces together to form the pattern
1389 arg_strings_pattern = ''.join(arg_string_pattern_parts)
1390
1391 # converts arg strings to the appropriate and then takes the action
1392 def take_action(action, argument_strings, option_string=None):
1393 argument_values = self._get_values(action, argument_strings)
1394 # take the action if we didn't receive a SUPPRESS value
1395 # (e.g. from a default)
1396 if argument_values is not SUPPRESS:
1397 action(self, namespace, argument_values, option_string)
1398
1399 # function to convert arg_strings into an optional action
1400 def consume_optional(start_index):
1401
1402 # determine the optional action and parse any explicit
1403 # argument out of the option string
1404 option_tuple = option_string_indices[start_index]
1405 action, option_string, explicit_arg = option_tuple
1406
1407 # loop because single-dash options can be chained
1408 # (e.g. -xyz is the same as -x -y -z if no args are required)
1409 match_argument = self._match_argument
1410 action_tuples = []
1411 while True:
1412
1413 # if we found no optional action, raise an error
1414 if action is None:
1415 self.error(_('no such option: %s') % option_string)
1416
1417 # if there is an explicit argument, try to match the
1418 # optional's string arguments to only this
1419 if explicit_arg is not None:
1420 arg_count = match_argument(action, 'A')
1421
1422 # if the action is a single-dash option and takes no
1423 # arguments, try to parse more single-dash options out
1424 # of the tail of the option string
1425 chars = self.prefix_chars
1426 if arg_count == 0 and option_string[1] not in chars:
1427 action_tuples.append((action, [], option_string))
1428 parse_optional = self._parse_optional
1429 for char in self.prefix_chars:
1430 option_string = char + explicit_arg
1431 option_tuple = parse_optional(option_string)
1432 if option_tuple[0] is not None:
1433 break
1434 else:
1435 msg = _('ignored explicit argument %r')
1436 raise ArgumentError(action, msg % explicit_arg)
1437
1438 # set the action, etc. for the next loop iteration
1439 action, option_string, explicit_arg = option_tuple
1440
1441 # if the action expect exactly one argument, we've
1442 # successfully matched the option; exit the loop
1443 elif arg_count == 1:
1444 stop = start_index + 1
1445 args = [explicit_arg]
1446 action_tuples.append((action, args, option_string))
1447 break
1448
1449 # error if a double-dash option did not use the
1450 # explicit argument
1451 else:
1452 msg = _('ignored explicit argument %r')
1453 raise ArgumentError(action, msg % explicit_arg)
1454
1455 # if there is no explicit argument, try to match the
1456 # optional's string arguments with the following strings
1457 # if successful, exit the loop
1458 else:
1459 start = start_index + 1
1460 selected_patterns = arg_strings_pattern[start:]
1461 arg_count = match_argument(action, selected_patterns)
1462 stop = start + arg_count
1463 args = arg_strings[start:stop]
1464 action_tuples.append((action, args, option_string))
1465 break
1466
1467 # add the Optional to the list and return the index at which
1468 # the Optional's string args stopped
1469 assert action_tuples
1470 for action, args, option_string in action_tuples:
1471 take_action(action, args, option_string)
1472 return stop
1473
1474 # the list of Positionals left to be parsed; this is modified
1475 # by consume_positionals()
1476 positionals = self._get_positional_actions()
1477
1478 # function to convert arg_strings into positional actions
1479 def consume_positionals(start_index):
1480 # match as many Positionals as possible
1481 match_partial = self._match_arguments_partial
1482 selected_pattern = arg_strings_pattern[start_index:]
1483 arg_counts = match_partial(positionals, selected_pattern)
1484
1485 # slice off the appropriate arg strings for each Positional
1486 # and add the Positional and its args to the list
1487 for action, arg_count in zip(positionals, arg_counts):
1488 args = arg_strings[start_index: start_index + arg_count]
1489 start_index += arg_count
1490 take_action(action, args)
1491
1492 # slice off the Positionals that we just parsed and return the
1493 # index at which the Positionals' string args stopped
1494 positionals[:] = positionals[len(arg_counts):]
1495 return start_index
1496
1497 # consume Positionals and Optionals alternately, until we have
1498 # passed the last option string
1499 start_index = 0
1500 if option_string_indices:
1501 max_option_string_index = max(option_string_indices)
1502 else:
1503 max_option_string_index = -1
1504 while start_index <= max_option_string_index:
1505
1506 # consume any Positionals preceding the next option
1507 next_option_string_index = min(
1508 index
1509 for index in option_string_indices
1510 if index >= start_index)
1511 if start_index != next_option_string_index:
1512 positionals_end_index = consume_positionals(start_index)
1513
1514 # only try to parse the next optional if we didn't consume
1515 # the option string during the positionals parsing
1516 if positionals_end_index > start_index:
1517 start_index = positionals_end_index
1518 continue
1519 else:
1520 start_index = positionals_end_index
1521
1522 # if we consumed all the positionals we could and we're not
1523 # at the index of an option string, there were unparseable
1524 # arguments
1525 if start_index not in option_string_indices:
1526 msg = _('extra arguments found: %s')
1527 extras = arg_strings[start_index:next_option_string_index]
1528 self.error(msg % ' '.join(extras))
1529
1530 # consume the next optional and any arguments for it
1531 start_index = consume_optional(start_index)
1532
1533 # consume any positionals following the last Optional
1534 stop_index = consume_positionals(start_index)
1535
1536 # if we didn't consume all the argument strings, there were too
1537 # many supplied
1538 if stop_index != len(arg_strings):
1539 extras = arg_strings[stop_index:]
1540 self.error(_('extra arguments found: %s') % ' '.join(extras))
1541
1542 # if we didn't use all the Positional objects, there were too few
1543 # arg strings supplied.
1544 if positionals:
1545 self.error(_('too few arguments'))
1546
1547 # return the updated namespace
1548 return namespace
1549
1550 def _match_argument(self, action, arg_strings_pattern):
1551 # match the pattern for this action to the arg strings
1552 nargs_pattern = self._get_nargs_pattern(action)
1553 match = _re.match(nargs_pattern, arg_strings_pattern)
1554
1555 # raise an exception if we weren't able to find a match
1556 if match is None:
1557 nargs_errors = {
1558 None:_('expected one argument'),
1559 OPTIONAL:_('expected at most one argument'),
1560 ONE_OR_MORE:_('expected at least one argument')
1561 }
1562 default = _('expected %s argument(s)') % action.nargs
1563 msg = nargs_errors.get(action.nargs, default)
1564 raise ArgumentError(action, msg)
1565
1566 # return the number of arguments matched
1567 return len(match.group(1))
1568
1569 def _match_arguments_partial(self, actions, arg_strings_pattern):
1570 # progressively shorten the actions list by slicing off the
1571 # final actions until we find a match
1572 result = []
1573 for i in xrange(len(actions), 0, -1):
1574 actions_slice = actions[:i]
1575 pattern = ''.join(self._get_nargs_pattern(action)
1576 for action in actions_slice)
1577 match = _re.match(pattern, arg_strings_pattern)
1578 if match is not None:
1579 result.extend(len(string) for string in match.groups())
1580 break
1581
1582 # return the list of arg string counts
1583 return result
1584
1585 def _parse_optional(self, arg_string):
1586 # if it doesn't start with a prefix, it was meant to be positional
1587 if not arg_string[0] in self.prefix_chars:
1588 return None
1589
1590 # if it's just dashes, it was meant to be positional
1591 if not arg_string.strip('-'):
1592 return None
1593
1594 # if the option string is present in the parser, return the action
1595 if arg_string in self._option_strings:
1596 action = self._option_strings[arg_string]
1597 return action, arg_string, None
1598
1599 # search through all possible prefixes of the option string
1600 # and all actions in the parser for possible interpretations
1601 option_tuples = []
1602 prefix_tuples = self._get_option_prefix_tuples(arg_string)
1603 for option_string in self._option_strings:
1604 for option_prefix, explicit_arg in prefix_tuples:
1605 if option_string.startswith(option_prefix):
1606 action = self._option_strings[option_string]
1607 tup = action, option_string, explicit_arg
1608 option_tuples.append(tup)
1609 break
1610
1611 # if multiple actions match, the option string was ambiguous
1612 if len(option_tuples) > 1:
1613 options = ', '.join(opt_str for _, opt_str, _ in option_tuples)
1614 tup = arg_string, options
1615 self.error(_('ambiguous option: %s could match %s') % tup)
1616
1617 # if exactly one action matched, this segmentation is good,
1618 # so return the parsed action
1619 elif len(option_tuples) == 1:
1620 option_tuple, = option_tuples
1621 return option_tuple
1622
1623 # if it was not found as an option, but it looks like a negative
1624 # number, it was meant to be positional
1625 if self._negative_number_matcher.match(arg_string):
1626 return None
1627
1628 # it was meant to be an optional but there is no such option
1629 # in this parser (though it might be a valid option in a subparser)
1630 return None, arg_string, None
1631
1632 def _get_option_prefix_tuples(self, option_string):
1633 result = []
1634
1635 # option strings starting with two prefix characters are only
1636 # split at the '='
1637 chars = self.prefix_chars
1638 if option_string[0] in chars and option_string[1] in chars:
1639 if '=' in option_string:
1640 option_prefix, explicit_arg = option_string.split('=', 1)
1641 else:
1642 option_prefix = option_string
1643 explicit_arg = None
1644 tup = option_prefix, explicit_arg
1645 result.append(tup)
1646
1647 # option strings starting with a single prefix character are
1648 # split at all indices
1649 else:
1650 for first_index, char in enumerate(option_string):
1651 if char not in self.prefix_chars:
1652 break
1653 for i in xrange(len(option_string), first_index, -1):
1654 tup = option_string[:i], option_string[i:] or None
1655 result.append(tup)
1656
1657 # return the collected prefix tuples
1658 return result
1659
1660 def _get_nargs_pattern(self, action):
1661 # in all examples below, we have to allow for '--' args
1662 # which are represented as '-' in the pattern
1663 nargs = action.nargs
1664
1665 # the default (None) is assumed to be a single argument
1666 if nargs is None:
1667 nargs_pattern = '(-*A-*)'
1668
1669 # allow zero or one arguments
1670 elif nargs == OPTIONAL:
1671 nargs_pattern = '(-*A?-*)'
1672
1673 # allow zero or more arguments
1674 elif nargs == ZERO_OR_MORE:
1675 nargs_pattern = '(-*[A-]*)'
1676
1677 # allow one or more arguments
1678 elif nargs == ONE_OR_MORE:
1679 nargs_pattern = '(-*A[A-]*)'
1680
1681 # allow one argument followed by any number of options or arguments
1682 elif nargs is PARSER:
1683 nargs_pattern = '(-*A[-AO]*)'
1684
1685 # all others should be integers
1686 else:
1687 nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs)
1688
1689 # if this is an optional action, -- is not allowed
1690 if action.option_strings:
1691 nargs_pattern = nargs_pattern.replace('-*', '')
1692 nargs_pattern = nargs_pattern.replace('-', '')
1693
1694 # return the pattern
1695 return nargs_pattern
1696
1697 # ========================
1698 # Value conversion methods
1699 # ========================
1700
1701 def _get_values(self, action, arg_strings):
1702 # for everything but PARSER args, strip out '--'
1703 if action.nargs is not PARSER:
1704 arg_strings = [s for s in arg_strings if s != '--']
1705
1706 # optional argument produces a default when not present
1707 if not arg_strings and action.nargs == OPTIONAL:
1708 if action.option_strings:
1709 value = action.const
1710 else:
1711 value = action.default
1712 if isinstance(value, basestring):
1713 value = self._get_value(action, value)
1714 self._check_value(action, value)
1715
1716 # when nargs='*' on a positional, if there were no command-line
1717 # args, use the default if it is anything other than None
1718 elif (not arg_strings and action.nargs == ZERO_OR_MORE and
1719 not action.option_strings):
1720 if action.default is not None:
1721 value = action.default
1722 else:
1723 value = arg_strings
1724 self._check_value(action, value)
1725
1726 # single argument or optional argument produces a single value
1727 elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
1728 arg_string, = arg_strings
1729 value = self._get_value(action, arg_string)
1730 self._check_value(action, value)
1731
1732 # PARSER arguments convert all values, but check only the first
1733 elif action.nargs is PARSER:
1734 value = list(self._get_value(action, v) for v in arg_strings)
1735 self._check_value(action, value[0])
1736
1737 # all other types of nargs produce a list
1738 else:
1739 value = list(self._get_value(action, v) for v in arg_strings)
1740 for v in value:
1741 self._check_value(action, v)
1742
1743 # return the converted value
1744 return value
1745
1746 def _get_value(self, action, arg_string):
1747 type_func = self._registry_get('type', action.type, action.type)
1748 if not callable(type_func):
1749 msg = _('%r is not callable')
1750 raise ArgumentError(action, msg % type_func)
1751
1752 # convert the value to the appropriate type
1753 try:
1754 result = type_func(arg_string)
1755
1756 # TypeErrors or ValueErrors indicate errors
1757 except (TypeError, ValueError):
1758 name = getattr(action.type, '__name__', repr(action.type))
1759 msg = _('invalid %s value: %r')
1760 raise ArgumentError(action, msg % (name, arg_string))
1761
1762 # return the converted value
1763 return result
1764
1765 def _check_value(self, action, value):
1766 # converted value must be one of the choices (if specified)
1767 if action.choices is not None and value not in action.choices:
1768 tup = value, ', '.join(map(repr, action.choices))
1769 msg = _('invalid choice: %r (choose from %s)') % tup
1770 raise ArgumentError(action, msg)
1771
1772
1773
1774 # =======================
1775 # Help-formatting methods
1776 # =======================
1777
1778 def format_usage(self):
1779 formatter = self._get_formatter()
1780 formatter.add_usage(self.usage,
1781 self._get_optional_actions(),
1782 self._get_positional_actions())
1783 return formatter.format_help()
1784
1785 def format_help(self):
1786 formatter = self._get_formatter()
1787
1788 # usage
1789 formatter.add_usage(self.usage,
1790 self._get_optional_actions(),
1791 self._get_positional_actions())
1792
1793 # description
1794 formatter.add_text(self.description)
1795
1796 # positionals
1797 formatter.start_section(_('positional arguments'))
1798 formatter.add_arguments(self._positional_actions_list)
1799 formatter.end_section()
1800
1801 # optionals
1802 formatter.start_section(_('optional arguments'))
1803 formatter.add_arguments(self._optional_actions_list)
1804 formatter.end_section()
1805
1806 # user-defined groups
1807 for argument_group in self._argument_groups:
1808 formatter.start_section(argument_group.title)
1809 formatter.add_text(argument_group.description)
1810 formatter.add_arguments(argument_group._positional_actions_list)
1811 formatter.add_arguments(argument_group._optional_actions_list)
1812 formatter.end_section()
1813
1814 # epilog
1815 formatter.add_text(self.epilog)
1816
1817 # determine help from format above
1818 return formatter.format_help()
1819
1820 def format_version(self):
1821 formatter = self._get_formatter()
1822 formatter.add_text(self.version)
1823 return formatter.format_help()
1824
1825 def _get_formatter(self):
1826 return self.formatter_class(prog=self.prog)
1827
1828 # =====================
1829 # Help-printing methods
1830 # =====================
1831
1832 def print_usage(self, file=None):
1833 self._print_message(self.format_usage(), file)
1834
1835 def print_help(self, file=None):
1836 self._print_message(self.format_help(), file)
1837
1838 def print_version(self, file=None):
1839 self._print_message(self.format_version(), file)
1840
1841 def _print_message(self, message, file=None):
1842 if message:
1843 if file is None:
1844 file = _sys.stderr
1845 file.write(message)
1846
1847
1848 # ===============
1849 # Exiting methods
1850 # ===============
1851
1852 def exit(self, status=0, message=None):
1853 if message:
1854 _sys.stderr.write(message)
1855 _sys.exit(status)
1856
1857 def error(self, message):
1858 """error(message: string)
1859
1860 Prints a usage message incorporating the message to stderr and
1861 exits.
1862
1863 If you override this in a subclass, it should not return -- it
1864 should either exit or raise an exception.
1865 """
1866 self.print_usage(_sys.stderr)
1867 self.exit(2, _('%s: error: %s\n') % (self.prog, message))
@@ -0,0 +1,33 b''
1 =========================================
2 Notes on the IPython configuration system
3 =========================================
4
5 This document has some random notes on the configuration system.
6
7 To start, an IPython process needs:
8
9 * Configuration files
10 * Command line options
11 * Additional files (FURL files, extra scripts, etc.)
12
13 It feeds these things into the core logic of the process, and as output,
14 produces:
15
16 * Log files
17 * Security files
18
19 There are a number of things that complicate this:
20
21 * A process may need to be started on a different host that doesn't have
22 any of the config files or additional files. Those files need to be
23 moved over and put in a staging area. The process then needs to be told
24 about them.
25 * The location of the output files should somehow be set by config files or
26 command line options.
27 * Our config files are very hierarchical, but command line options are flat,
28 making it difficult to relate command line options to config files.
29 * Some processes (like ipcluster and the daemons) have to manage the input and
30 output files for multiple different subprocesses, each possibly on a
31 different host. Ahhhh!
32 * Our configurations are not singletons. A given user will likely have
33 many different configurations for different clusters.
@@ -0,0 +1,251 b''
1 .. _parallel_process:
2
3 ===========================================
4 Starting the IPython controller and engines
5 ===========================================
6
7 To use IPython for parallel computing, you need to start one instance of
8 the controller and one or more instances of the engine. The controller
9 and each engine can run on different machines or on the same machine.
10 Because of this, there are many different possibilities.
11
12 Broadly speaking, there are two ways of going about starting a controller and engines:
13
14 * In an automated manner using the :command:`ipcluster` command.
15 * In a more manual way using the :command:`ipcontroller` and
16 :command:`ipengine` commands.
17
18 This document describes both of these methods. We recommend that new users start with the :command:`ipcluster` command as it simplifies many common usage cases.
19
20 General considerations
21 ======================
22
23 Before delving into the details about how you can start a controller and engines using the various methods, we outline some of the general issues that come up when starting the controller and engines. These things come up no matter which method you use to start your IPython cluster.
24
25 Let's say that you want to start the controller on ``host0`` and engines on hosts ``host1``-``hostn``. The following steps are then required:
26
27 1. Start the controller on ``host0`` by running :command:`ipcontroller` on
28 ``host0``.
29 2. Move the FURL file (:file:`ipcontroller-engine.furl`) created by the
30 controller from ``host0`` to hosts ``host1``-``hostn``.
31 3. Start the engines on hosts ``host1``-``hostn`` by running
32 :command:`ipengine`. This command has to be told where the FURL file
33 (:file:`ipcontroller-engine.furl`) is located.
34
35 At this point, the controller and engines will be connected. By default, the
36 FURL files created by the controller are put into the
37 :file:`~/.ipython/security` directory. If the engines share a filesystem with
38 the controller, step 2 can be skipped as the engines will automatically look
39 at that location.
40
41 The final step required required to actually use the running controller from a
42 client is to move the FURL files :file:`ipcontroller-mec.furl` and
43 :file:`ipcontroller-tc.furl` from ``host0`` to the host where the clients will
44 be run. If these file are put into the :file:`~/.ipython/security` directory of the client's host, they will be found automatically. Otherwise, the full path to them has to be passed to the client's constructor.
45
46 Using :command:`ipcluster`
47 ==========================
48
49 The :command:`ipcluster` command provides a simple way of starting a controller and engines in the following situations:
50
51 1. When the controller and engines are all run on localhost. This is useful
52 for testing or running on a multicore computer.
53 2. When engines are started using the :command:`mpirun` command that comes
54 with most MPI [MPI]_ implementations
55 3. When engines are started using the PBS [PBS]_ batch system.
56
57 .. note::
58
59 It is also possible for advanced users to add support to
60 :command:`ipcluster` for starting controllers and engines using other
61 methods (like Sun's Grid Engine for example).
62
63 .. note::
64
65 Currently :command:`ipcluster` requires that the
66 :file:`~/.ipython/security` directory live on a shared filesystem that is
67 seen by both the controller and engines. If you don't have a shared file
68 system you will need to use :command:`ipcontroller` and
69 :command:`ipengine` directly.
70
71 Underneath the hood, :command:`ipcluster` just uses :command:`ipcontroller`
72 and :command:`ipengine` to perform the steps described above.
73
74 Using :command:`ipcluster` in local mode
75 ----------------------------------------
76
77 To start one controller and 4 engines on localhost, just do::
78
79 $ ipcluster local -n 4
80
81 To see other command line options for the local mode, do::
82
83 $ ipcluster local -h
84
85 Using :command:`ipcluster` in mpirun mode
86 -----------------------------------------
87
88 The mpirun mode is useful if you:
89
90 1. Have MPI installed.
91 2. Your systems are configured to use the :command:`mpirun` command to start
92 processes.
93
94 If these are satisfied, you can start an IPython cluster using::
95
96 $ ipcluster mpirun -n 4
97
98 This does the following:
99
100 1. Starts the IPython controller on current host.
101 2. Uses :command:`mpirun` to start 4 engines.
102
103 On newer MPI implementations (such as OpenMPI), this will work even if you don't make any calls to MPI or call :func:`MPI_Init`. However, older MPI implementations actually require each process to call :func:`MPI_Init` upon starting. The easiest way of having this done is to install the mpi4py [mpi4py]_ package and then call ipcluster with the ``--mpi`` option::
104
105 $ ipcluster mpirun -n 4 --mpi=mpi4py
106
107 Unfortunately, even this won't work for some MPI implementations. If you are having problems with this, you will likely have to use a custom Python executable that itself calls :func:`MPI_Init` at the appropriate time. Fortunately, mpi4py comes with such a custom Python executable that is easy to install and use. However, this custom Python executable approach will not work with :command:`ipcluster` currently.
108
109 Additional command line options for this mode can be found by doing::
110
111 $ ipcluster mpirun -h
112
113 More details on using MPI with IPython can be found :ref:`here <parallelmpi>`.
114
115
116 Using :command:`ipcluster` in PBS mode
117 --------------------------------------
118
119 The PBS mode uses the Portable Batch System [PBS]_ to start the engines. To use this mode, you first need to create a PBS script template that will be used to start the engines. Here is a sample PBS script template:
120
121 .. sourcecode:: bash
122
123 #PBS -N ipython
124 #PBS -j oe
125 #PBS -l walltime=00:10:00
126 #PBS -l nodes=${n/4}:ppn=4
127 #PBS -q parallel
128
129 cd $$PBS_O_WORKDIR
130 export PATH=$$HOME/usr/local/bin
131 export PYTHONPATH=$$HOME/usr/local/lib/python2.4/site-packages
132 /usr/local/bin/mpiexec -n ${n} ipengine --logfile=$$PBS_O_WORKDIR/ipengine
133
134 There are a few important points about this template:
135
136 1. This template will be rendered at runtime using IPython's :mod:`Itpl`
137 template engine.
138
139 2. Instead of putting in the actual number of engines, use the notation
140 ``${n}`` to indicate the number of engines to be started. You can also uses
141 expressions like ``${n/4}`` in the template to indicate the number of
142 nodes.
143
144 3. Because ``$`` is a special character used by the template engine, you must
145 escape any ``$`` by using ``$$``. This is important when referring to
146 environment variables in the template.
147
148 4. Any options to :command:`ipengine` should be given in the batch script
149 template.
150
151 5. Depending on the configuration of you system, you may have to set
152 environment variables in the script template.
153
154 Once you have created such a script, save it with a name like :file:`pbs.template`. Now you are ready to start your job::
155
156 $ ipcluster pbs -n 128 --pbs-script=pbs.template
157
158 Additional command line options for this mode can be found by doing::
159
160 $ ipcluster pbs -h
161
162 Using the :command:`ipcontroller` and :command:`ipengine` commands
163 ==================================================================
164
165 It is also possible to use the :command:`ipcontroller` and :command:`ipengine` commands to start your controller and engines. This approach gives you full control over all aspects of the startup process.
166
167 Starting the controller and engine on your local machine
168 --------------------------------------------------------
169
170 To use :command:`ipcontroller` and :command:`ipengine` to start things on your
171 local machine, do the following.
172
173 First start the controller::
174
175 $ ipcontroller
176
177 Next, start however many instances of the engine you want using (repeatedly) the command::
178
179 $ ipengine
180
181 The engines should start and automatically connect to the controller using the FURL files in :file:`~./ipython/security`. You are now ready to use the controller and engines from IPython.
182
183 .. warning::
184
185 The order of the above operations is very important. You *must*
186 start the controller before the engines, since the engines connect
187 to the controller as they get started.
188
189 .. note::
190
191 On some platforms (OS X), to put the controller and engine into the
192 background you may need to give these commands in the form ``(ipcontroller
193 &)`` and ``(ipengine &)`` (with the parentheses) for them to work
194 properly.
195
196 Starting the controller and engines on different hosts
197 ------------------------------------------------------
198
199 When the controller and engines are running on different hosts, things are
200 slightly more complicated, but the underlying ideas are the same:
201
202 1. Start the controller on a host using :command:`ipcontroller`.
203 2. Copy :file:`ipcontroller-engine.furl` from :file:`~./ipython/security` on the controller's host to the host where the engines will run.
204 3. Use :command:`ipengine` on the engine's hosts to start the engines.
205
206 The only thing you have to be careful of is to tell :command:`ipengine` where the :file:`ipcontroller-engine.furl` file is located. There are two ways you can do this:
207
208 * Put :file:`ipcontroller-engine.furl` in the :file:`~./ipython/security`
209 directory on the engine's host, where it will be found automatically.
210 * Call :command:`ipengine` with the ``--furl-file=full_path_to_the_file``
211 flag.
212
213 The ``--furl-file`` flag works like this::
214
215 $ ipengine --furl-file=/path/to/my/ipcontroller-engine.furl
216
217 .. note::
218
219 If the controller's and engine's hosts all have a shared file system
220 (:file:`~./ipython/security` is the same on all of them), then things
221 will just work!
222
223 Make FURL files persistent
224 ---------------------------
225
226 At fist glance it may seem that that managing the FURL files is a bit annoying. Going back to the house and key analogy, copying the FURL around each time you start the controller is like having to make a new key every time you want to unlock the door and enter your house. As with your house, you want to be able to create the key (or FURL file) once, and then simply use it at any point in the future.
227
228 This is possible. The only thing you have to do is decide what ports the controller will listen on for the engines and clients. This is done as follows::
229
230 $ ipcontroller -r --client-port=10101 --engine-port=10102
231
232 Then, just copy the furl files over the first time and you are set. You can start and stop the controller and engines any many times as you want in the future, just make sure to tell the controller to use the *same* ports.
233
234 .. note::
235
236 You may ask the question: what ports does the controller listen on if you
237 don't tell is to use specific ones? The default is to use high random port
238 numbers. We do this for two reasons: i) to increase security through
239 obscurity and ii) to multiple controllers on a given host to start and
240 automatically use different ports.
241
242 Log files
243 ---------
244
245 All of the components of IPython have log files associated with them.
246 These log files can be extremely useful in debugging problems with
247 IPython and can be found in the directory :file:`~/.ipython/log`. Sending
248 the log files to us will often help us to debug any problems.
249
250
251 .. [PBS] Portable Batch System. http://www.openpbs.org/
@@ -0,0 +1,363 b''
1 .. _parallelsecurity:
2
3 ===========================
4 Security details of IPython
5 ===========================
6
7 IPython's :mod:`IPython.kernel` package exposes the full power of the Python
8 interpreter over a TCP/IP network for the purposes of parallel computing. This
9 feature brings up the important question of IPython's security model. This
10 document gives details about this model and how it is implemented in IPython's
11 architecture.
12
13 Processs and network topology
14 =============================
15
16 To enable parallel computing, IPython has a number of different processes that
17 run. These processes are discussed at length in the IPython documentation and
18 are summarized here:
19
20 * The IPython *engine*. This process is a full blown Python
21 interpreter in which user code is executed. Multiple
22 engines are started to make parallel computing possible.
23 * The IPython *controller*. This process manages a set of
24 engines, maintaining a queue for each and presenting
25 an asynchronous interface to the set of engines.
26 * The IPython *client*. This process is typically an
27 interactive Python process that is used to coordinate the
28 engines to get a parallel computation done.
29
30 Collectively, these three processes are called the IPython *kernel*.
31
32 These three processes communicate over TCP/IP connections with a well defined
33 topology. The IPython controller is the only process that listens on TCP/IP
34 sockets. Upon starting, an engine connects to a controller and registers
35 itself with the controller. These engine/controller TCP/IP connections persist
36 for the lifetime of each engine.
37
38 The IPython client also connects to the controller using one or more TCP/IP
39 connections. These connections persist for the lifetime of the client only.
40
41 A given IPython controller and set of engines typically has a relatively short
42 lifetime. Typically this lifetime corresponds to the duration of a single
43 parallel simulation performed by a single user. Finally, the controller,
44 engines and client processes typically execute with the permissions of that
45 same user. More specifically, the controller and engines are *not* executed as
46 root or with any other superuser permissions.
47
48 Application logic
49 =================
50
51 When running the IPython kernel to perform a parallel computation, a user
52 utilizes the IPython client to send Python commands and data through the
53 IPython controller to the IPython engines, where those commands are executed
54 and the data processed. The design of IPython ensures that the client is the
55 only access point for the capabilities of the engines. That is, the only way of addressing the engines is through a client.
56
57 A user can utilize the client to instruct the IPython engines to execute
58 arbitrary Python commands. These Python commands can include calls to the
59 system shell, access the filesystem, etc., as required by the user's
60 application code. From this perspective, when a user runs an IPython engine on
61 a host, that engine has the same capabilities and permissions as the user
62 themselves (as if they were logged onto the engine's host with a terminal).
63
64 Secure network connections
65 ==========================
66
67 Overview
68 --------
69
70 All TCP/IP connections between the client and controller as well as the
71 engines and controller are fully encrypted and authenticated. This section
72 describes the details of the encryption and authentication approached used
73 within IPython.
74
75 IPython uses the Foolscap network protocol [Foolscap]_ for all communications
76 between processes. Thus, the details of IPython's security model are directly
77 related to those of Foolscap. Thus, much of the following discussion is
78 actually just a discussion of the security that is built in to Foolscap.
79
80 Encryption
81 ----------
82
83 For encryption purposes, IPython and Foolscap use the well known Secure Socket
84 Layer (SSL) protocol [RFC5246]_. We use the implementation of this protocol
85 provided by the OpenSSL project through the pyOpenSSL [pyOpenSSL]_ Python
86 bindings to OpenSSL.
87
88 Authentication
89 --------------
90
91 IPython clients and engines must also authenticate themselves with the
92 controller. This is handled in a capabilities based security model
93 [Capability]_. In this model, the controller creates a strong cryptographic
94 key or token that represents each set of capability that the controller
95 offers. Any party who has this key and presents it to the controller has full
96 access to the corresponding capabilities of the controller. This model is
97 analogous to using a physical key to gain access to physical items
98 (capabilities) behind a locked door.
99
100 For a capabilities based authentication system to prevent unauthorized access,
101 two things must be ensured:
102
103 * The keys must be cryptographically strong. Otherwise attackers could gain
104 access by a simple brute force key guessing attack.
105 * The actual keys must be distributed only to authorized parties.
106
107 The keys in Foolscap are called Foolscap URL's or FURLs. The following section
108 gives details about how these FURLs are created in Foolscap. The IPython
109 controller creates a number of FURLs for different purposes:
110
111 * One FURL that grants IPython engines access to the controller. Also
112 implicit in this access is permission to execute code sent by an
113 authenticated IPython client.
114 * Two or more FURLs that grant IPython clients access to the controller.
115 Implicit in this access is permission to give the controller's engine code
116 to execute.
117
118 Upon starting, the controller creates these different FURLS and writes them
119 files in the user-read-only directory :file:`$HOME/.ipython/security`. Thus, only the
120 user who starts the controller has access to the FURLs.
121
122 For an IPython client or engine to authenticate with a controller, it must
123 present the appropriate FURL to the controller upon connecting. If the
124 FURL matches what the controller expects for a given capability, access is
125 granted. If not, access is denied. The exchange of FURLs is done after
126 encrypted communications channels have been established to prevent attackers
127 from capturing them.
128
129 .. note::
130
131 The FURL is similar to an unsigned private key in SSH.
132
133 Details of the Foolscap handshake
134 ---------------------------------
135
136 In this section we detail the precise security handshake that takes place at
137 the beginning of any network connection in IPython. For the purposes of this
138 discussion, the SERVER is the IPython controller process and the CLIENT is the
139 IPython engine or client process.
140
141 Upon starting, all IPython processes do the following:
142
143 1. Create a public key x509 certificate (ISO/IEC 9594).
144 2. Create a hash of the contents of the certificate using the SHA-1 algorithm.
145 The base-32 encoded version of this hash is saved by the process as its
146 process id (actually in Foolscap, this is the Tub id, but here refer to
147 it as the process id).
148
149 Upon starting, the IPython controller also does the following:
150
151 1. Save the x509 certificate to disk in a secure location. The CLIENT
152 certificate is never saved to disk.
153 2. Create a FURL for each capability that the controller has. There are
154 separate capabilities the controller offers for clients and engines. The
155 FURL is created using: a) the process id of the SERVER, b) the IP
156 address and port the SERVER is listening on and c) a 160 bit,
157 cryptographically secure string that represents the capability (the
158 "capability id").
159 3. The FURLs are saved to disk in a secure location on the SERVER's host.
160
161 For a CLIENT to be able to connect to the SERVER and access a capability of
162 that SERVER, the CLIENT must have knowledge of the FURL for that SERVER's
163 capability. This typically requires that the file containing the FURL be
164 moved from the SERVER's host to the CLIENT's host. This is done by the end
165 user who started the SERVER and wishes to have a CLIENT connect to the SERVER.
166
167 When a CLIENT connects to the SERVER, the following handshake protocol takes
168 place:
169
170 1. The CLIENT tells the SERVER what process (or Tub) id it expects the SERVER
171 to have.
172 2. If the SERVER has that process id, it notifies the CLIENT that it will now
173 enter encrypted mode. If the SERVER has a different id, the SERVER aborts.
174 3. Both CLIENT and SERVER initiate the SSL handshake protocol.
175 4. Both CLIENT and SERVER request the certificate of their peer and verify
176 that certificate. If this succeeds, all further communications are
177 encrypted.
178 5. Both CLIENT and SERVER send a hello block containing connection parameters
179 and their process id.
180 6. The CLIENT and SERVER check that their peer's stated process id matches the
181 hash of the x509 certificate the peer presented. If not, the connection is
182 aborted.
183 7. The CLIENT verifies that the SERVER's stated id matches the id of the
184 SERVER the CLIENT is intending to connect to. If not, the connection is
185 aborted.
186 8. The CLIENT and SERVER elect a master who decides on the final connection
187 parameters.
188
189 The public/private key pair associated with each process's x509 certificate
190 are completely hidden from this handshake protocol. There are however, used
191 internally by OpenSSL as part of the SSL handshake protocol. Each process
192 keeps their own private key hidden and sends its peer only the public key
193 (embedded in the certificate).
194
195 Finally, when the CLIENT requests access to a particular SERVER capability,
196 the following happens:
197
198 1. The CLIENT asks the SERVER for access to a capability by presenting that
199 capabilities id.
200 2. If the SERVER has a capability with that id, access is granted. If not,
201 access is not granted.
202 3. Once access has been gained, the CLIENT can use the capability.
203
204 Specific security vulnerabilities
205 =================================
206
207 There are a number of potential security vulnerabilities present in IPython's
208 architecture. In this section we discuss those vulnerabilities and detail how
209 the security architecture described above prevents them from being exploited.
210
211 Unauthorized clients
212 --------------------
213
214 The IPython client can instruct the IPython engines to execute arbitrary
215 Python code with the permissions of the user who started the engines. If an
216 attacker were able to connect their own hostile IPython client to the IPython
217 controller, they could instruct the engines to execute code.
218
219 This attack is prevented by the capabilities based client authentication
220 performed after the encrypted channel has been established. The relevant
221 authentication information is encoded into the FURL that clients must
222 present to gain access to the IPython controller. By limiting the distribution
223 of those FURLs, a user can grant access to only authorized persons.
224
225 It is highly unlikely that a client FURL could be guessed by an attacker
226 in a brute force guessing attack. A given instance of the IPython controller
227 only runs for a relatively short amount of time (on the order of hours). Thus
228 an attacker would have only a limited amount of time to test a search space of
229 size 2**320. Furthermore, even if a controller were to run for a longer amount
230 of time, this search space is quite large (larger for instance than that of
231 typical username/password pair).
232
233 Unauthorized engines
234 --------------------
235
236 If an attacker were able to connect a hostile engine to a user's controller,
237 the user might unknowingly send sensitive code or data to the hostile engine.
238 This attacker's engine would then have full access to that code and data.
239
240 This type of attack is prevented in the same way as the unauthorized client
241 attack, through the usage of the capabilities based authentication scheme.
242
243 Unauthorized controllers
244 ------------------------
245
246 It is also possible that an attacker could try to convince a user's IPython
247 client or engine to connect to a hostile IPython controller. That controller
248 would then have full access to the code and data sent between the IPython
249 client and the IPython engines.
250
251 Again, this attack is prevented through the FURLs, which ensure that a
252 client or engine connects to the correct controller. It is also important to
253 note that the FURLs also encode the IP address and port that the
254 controller is listening on, so there is little chance of mistakenly connecting
255 to a controller running on a different IP address and port.
256
257 When starting an engine or client, a user must specify which FURL to use
258 for that connection. Thus, in order to introduce a hostile controller, the
259 attacker must convince the user to use the FURLs associated with the
260 hostile controller. As long as a user is diligent in only using FURLs from
261 trusted sources, this attack is not possible.
262
263 Other security measures
264 =======================
265
266 A number of other measures are taken to further limit the security risks
267 involved in running the IPython kernel.
268
269 First, by default, the IPython controller listens on random port numbers.
270 While this can be overridden by the user, in the default configuration, an
271 attacker would have to do a port scan to even find a controller to attack.
272 When coupled with the relatively short running time of a typical controller
273 (on the order of hours), an attacker would have to work extremely hard and
274 extremely *fast* to even find a running controller to attack.
275
276 Second, much of the time, especially when run on supercomputers or clusters,
277 the controller is running behind a firewall. Thus, for engines or client to
278 connect to the controller:
279
280 * The different processes have to all be behind the firewall.
281
282 or:
283
284 * The user has to use SSH port forwarding to tunnel the
285 connections through the firewall.
286
287 In either case, an attacker is presented with addition barriers that prevent
288 attacking or even probing the system.
289
290 Summary
291 =======
292
293 IPython's architecture has been carefully designed with security in mind. The
294 capabilities based authentication model, in conjunction with the encrypted
295 TCP/IP channels, address the core potential vulnerabilities in the system,
296 while still enabling user's to use the system in open networks.
297
298 Other questions
299 ===============
300
301 About keys
302 ----------
303
304 Can you clarify the roles of the certificate and its keys versus the FURL,
305 which is also called a key?
306
307 The certificate created by IPython processes is a standard public key x509
308 certificate, that is used by the SSL handshake protocol to setup encrypted
309 channel between the controller and the IPython engine or client. This public
310 and private key associated with this certificate are used only by the SSL
311 handshake protocol in setting up this encrypted channel.
312
313 The FURL serves a completely different and independent purpose from the
314 key pair associated with the certificate. When we refer to a FURL as a
315 key, we are using the word "key" in the capabilities based security model
316 sense. This has nothing to do with "key" in the public/private key sense used
317 in the SSL protocol.
318
319 With that said the FURL is used as an cryptographic key, to grant
320 IPython engines and clients access to particular capabilities that the
321 controller offers.
322
323 Self signed certificates
324 ------------------------
325
326 Is the controller creating a self-signed certificate? Is this created for per
327 instance/session, one-time-setup or each-time the controller is started?
328
329 The Foolscap network protocol, which handles the SSL protocol details, creates
330 a self-signed x509 certificate using OpenSSL for each IPython process. The
331 lifetime of the certificate is handled differently for the IPython controller
332 and the engines/client.
333
334 For the IPython engines and client, the certificate is only held in memory for
335 the lifetime of its process. It is never written to disk.
336
337 For the controller, the certificate can be created anew each time the
338 controller starts or it can be created once and reused each time the
339 controller starts. If at any point, the certificate is deleted, a new one is
340 created the next time the controller starts.
341
342 SSL private key
343 ---------------
344
345 How the private key (associated with the certificate) is distributed?
346
347 In the usual implementation of the SSL protocol, the private key is never
348 distributed. We follow this standard always.
349
350 SSL versus Foolscap authentication
351 ----------------------------------
352
353 Many SSL connections only perform one sided authentication (the server to the
354 client). How is the client authentication in IPython's system related to SSL
355 authentication?
356
357 We perform a two way SSL handshake in which both parties request and verify
358 the certificate of their peer. This mutual authentication is handled by the
359 SSL handshake and is separate and independent from the additional
360 authentication steps that the CLIENT and SERVER perform after an encrypted
361 channel is established.
362
363 .. [RFC5246] <http://tools.ietf.org/html/rfc5246>
@@ -0,0 +1,172 b''
1 #!/usr/bin/env python
2 """A parallel tasking tool that uses asynchronous programming. This uses
3 blocking client to get taskid, but returns a Deferred as the result of
4 run(). Users should attach their callbacks on these Deferreds.
5
6 Only returning of results is asynchronous. Submitting tasks and getting task
7 ids are done synchronously.
8
9 Yichun Wei 03/2008
10 """
11
12 import inspect
13 import itertools
14 import numpy as N
15
16 from twisted.python import log
17 from ipython1.kernel import client
18 from ipython1.kernel.client import Task
19
20 """ After http://trac.pocoo.org/repos/pocoo/trunk/pocoo/utils/decorators.py
21 """
22 class submit_job(object):
23 """ a decorator factory: takes a MultiEngineClient a TaskClient, returns a
24 decorator, that makes a call to the decorated func as a task in ipython1
25 and submit it to IPython1 controller:
26 """
27 def __init__(self, rc, tc):
28 self.rc = rc
29 self.tc = tc
30
31 def __call__(self, func):
32 return self._decorate(func)
33
34 def _getinfo(self, func):
35 assert inspect.ismethod(func) or inspect.isfunction(func)
36 regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
37 argnames = list(regargs)
38 if varargs:
39 argnames.append(varargs)
40 if varkwargs:
41 argnames.append(varkwargs)
42 counter = itertools.count()
43 fullsign = inspect.formatargspec(
44 regargs, varargs, varkwargs, defaults,
45 formatvalue=lambda value: '=defarg[%i]' % counter.next())[1:-1]
46 shortsign = inspect.formatargspec(
47 regargs, varargs, varkwargs, defaults,
48 formatvalue=lambda value: '')[1:-1]
49 dic = dict(('arg%s' % n, name) for n, name in enumerate(argnames))
50 dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
51 fullsign = fullsign, defarg = func.func_defaults or ())
52 return dic
53
54 def _decorate(self, func):
55 """
56 Takes a function and a remote controller and returns a function
57 decorated that is going to submit the job with the controller.
58 The decorated function is obtained by evaluating a lambda
59 function with the correct signature.
60
61 the TaskController setupNS doesn't cope with functions, but we
62 can use RemoteController to push functions/modules into engines.
63
64 Changes:
65 200803. In new ipython1, we use push_function for functions.
66 """
67 rc, tc = self.rc, self.tc
68 infodict = self._getinfo(func)
69 if 'rc' in infodict['argnames']:
70 raise NameError, "You cannot use rc as argument names!"
71
72 # we assume the engines' namepace has been prepared.
73 # ns[func.__name__] is already the decorated closure function.
74 # we need to change it back to the original function:
75 ns = {}
76 ns[func.__name__] = func
77
78 # push func and all its environment/prerequesites to engines
79 rc.push_function(ns, block=True) # note it is nonblock by default, not know if it causes problems
80
81 def do_submit_func(*args, **kwds):
82 jobns = {}
83
84 # Initialize job namespace with args that have default args
85 # now we support calls that uses default args
86 for n in infodict['fullsign'].split(','):
87 try:
88 vname, var = n.split('=')
89 vname, var = vname.strip(), var.strip()
90 except: # no defarg, one of vname, var is None
91 pass
92 else:
93 jobns.setdefault(vname, eval(var, infodict))
94
95 # push args and kwds, overwritting default args if needed.
96 nokwds = dict((n,v) for n,v in zip(infodict['argnames'], args)) # truncated
97 jobns.update(nokwds)
98 jobns.update(kwds)
99
100 task = Task('a_very_long_and_rare_name = %(name)s(%(shortsign)s)' % infodict,
101 pull=['a_very_long_and_rare_name'], push=jobns,)
102 jobid = tc.run(task)
103 # res is a deferred, one can attach callbacks on it
104 res = tc.task_controller.get_task_result(jobid, block=True)
105 res.addCallback(lambda x: x.ns['a_very_long_and_rare_name'])
106 res.addErrback(log.err)
107 return res
108
109 do_submit_func.rc = rc
110 do_submit_func.tc = tc
111 return do_submit_func
112
113
114 def parallelized(rc, tc, initstrlist=[]):
115 """ rc - remote controller
116 tc - taks controller
117 strlist - a list of str that's being executed on engines.
118 """
119 for cmd in initstrlist:
120 rc.execute(cmd, block=True)
121 return submit_job(rc, tc)
122
123
124 from twisted.internet import defer
125 from numpy import array, nan
126
127 def pmap(func, parr, **kwds):
128 """Run func on every element of parr (array), using the elements
129 as the only one parameter (so you can usually use a dict that
130 wraps many parameters). -> a result array of Deferreds with the
131 same shape. func.tc will be used as the taskclient.
132
133 **kwds are passed on to func, not changed.
134 """
135 assert func.tc
136 tc = func.tc
137
138 def run(p, **kwds):
139 if p:
140 return func(p, **kwds)
141 else:
142 return defer.succeed(nan)
143
144 reslist = [run(p, **kwds).addErrback(log.err) for p in parr.flat]
145 resarr = array(reslist)
146 resarr.shape = parr.shape
147 return resarr
148
149
150 if __name__=='__main__':
151
152 rc = client.MultiEngineClient(client.default_address)
153 tc = client.TaskClient(client.default_task_address)
154
155 # if commenting out the decorator you get a local running version
156 # instantly
157 @parallelized(rc, tc)
158 def f(a, b=1):
159 #from time import sleep
160 #sleep(1)
161 print "a,b=", a,b
162 return a+b
163
164 def showres(x):
165 print 'ans:',x
166
167 res = f(11,5)
168 res.addCallback(showres)
169
170 # this is not necessary in Twisted 8.0
171 from twisted.internet import reactor
172 reactor.run()
@@ -0,0 +1,119 b''
1 import types
2
3 class AttributeBase(object):
4
5 def __get__(self, inst, cls=None):
6 if inst is None:
7 return self
8 try:
9 return inst._attributes[self.name]
10 except KeyError:
11 raise AttributeError("object has no attribute %r" % self.name)
12
13 def __set__(self, inst, value):
14 actualValue = self.validate(inst, self.name, value)
15 inst._attributes[self.name] = actualValue
16
17 def validate(self, inst, name, value):
18 raise NotImplementedError("validate must be implemented by a subclass")
19
20 class NameFinder(type):
21
22 def __new__(cls, name, bases, classdict):
23 attributeList = []
24 for k,v in classdict.iteritems():
25 if isinstance(v, AttributeBase):
26 v.name = k
27 attributeList.append(k)
28 classdict['_attributeList'] = attributeList
29 return type.__new__(cls, name, bases, classdict)
30
31 class HasAttributes(object):
32 __metaclass__ = NameFinder
33
34 def __init__(self):
35 self._attributes = {}
36
37 def getAttributeNames(self):
38 return self._attributeList
39
40 def getAttributesOfType(self, t, default=None):
41 result = {}
42 for a in self._attributeList:
43 if self.__class__.__dict__[a].__class__ == t:
44 try:
45 value = getattr(self, a)
46 except AttributeError:
47 value = None
48 result[a] = value
49 return result
50
51 class TypedAttribute(AttributeBase):
52
53 def validate(self, inst, name, value):
54 if type(value) != self._type:
55 raise TypeError("attribute %s must be of type %s" % (name, self._type))
56 else:
57 return value
58
59 # class Option(TypedAttribute):
60 #
61 # _type = types.IntType
62 #
63 # class Param(TypedAttribute):
64 #
65 # _type = types.FloatType
66 #
67 # class String(TypedAttribute):
68 #
69 # _type = types.StringType
70
71 class TypedSequenceAttribute(AttributeBase):
72
73 def validate(self, inst, name, value):
74 if type(value) != types.TupleType and type(value) != types.ListType:
75 raise TypeError("attribute %s must be a list or tuple" % (name))
76 else:
77 for item in value:
78 if type(item) != self._subtype:
79 raise TypeError("attribute %s must be a list or tuple of items with type %s" % (name, self._subtype))
80 return value
81
82 # class Instance(AttributeBase):
83 #
84 # def __init__(self, cls):
85 # self.cls = cls
86 #
87 # def validate(self, inst, name, value):
88 # if not isinstance(value, self.cls):
89 # raise TypeError("attribute %s must be an instance of class %s" % (name, self.cls))
90 # else:
91 # return value
92
93
94 # class OptVec(TypedSequenceAttribute):
95 #
96 # _subtype = types.IntType
97 #
98 # class PrmVec(TypedSequenceAttribute):
99 #
100 # _subtype = types.FloatType
101 #
102 # class StrVec(TypedSequenceAttribute):
103 #
104 # _subtype = types.StringType
105 #
106 #
107 # class Bar(HasAttributes):
108 #
109 # a = Option()
110 #
111 # class Foo(HasAttributes):
112 #
113 # a = Option()
114 # b = Param()
115 # c = String()
116 # d = OptVec()
117 # e = PrmVec()
118 # f = StrVec()
119 # h = Instance(Bar) No newline at end of file
@@ -61,6 +61,7 b' controller_config = dict('
61
61
62 logfile = '', # Empty means log to stdout
62 logfile = '', # Empty means log to stdout
63 import_statement = '',
63 import_statement = '',
64 reuse_furls = False, # If False, old furl files are deleted
64
65
65 engine_tub = dict(
66 engine_tub = dict(
66 ip = '', # Empty string means all interfaces
67 ip = '', # Empty string means all interfaces
@@ -18,7 +18,8 b' __docformat__ = "restructuredtext en"'
18 import os
18 import os
19 import cPickle as pickle
19 import cPickle as pickle
20
20
21 from twisted.python import log
21 from twisted.python import log, failure
22 from twisted.internet import defer
22
23
23 from IPython.kernel.fcutil import find_furl
24 from IPython.kernel.fcutil import find_furl
24 from IPython.kernel.enginefc import IFCEngine
25 from IPython.kernel.enginefc import IFCEngine
@@ -62,13 +63,17 b' class EngineConnector(object):'
62 self.tub.startService()
63 self.tub.startService()
63 self.engine_service = engine_service
64 self.engine_service = engine_service
64 self.engine_reference = IFCEngine(self.engine_service)
65 self.engine_reference = IFCEngine(self.engine_service)
65 self.furl = find_furl(furl_or_file)
66 try:
67 self.furl = find_furl(furl_or_file)
68 except ValueError:
69 return defer.fail(failure.Failure())
70 # return defer.fail(failure.Failure(ValueError('not a valid furl or furl file: %r' % furl_or_file)))
66 d = self.tub.getReference(self.furl)
71 d = self.tub.getReference(self.furl)
67 d.addCallbacks(self._register, self._log_failure)
72 d.addCallbacks(self._register, self._log_failure)
68 return d
73 return d
69
74
70 def _log_failure(self, reason):
75 def _log_failure(self, reason):
71 log.err('engine registration failed:')
76 log.err('EngineConnector: engine registration failed:')
72 log.err(reason)
77 log.err(reason)
73 return reason
78 return reason
74
79
This diff has been collapsed as it changes many lines, (749 lines changed) Show them Hide them
@@ -1,347 +1,486 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """Start an IPython cluster conveniently, either locally or remotely.
4 """Start an IPython cluster = (controller + engines)."""
5
5
6 Basic usage
6 #-----------------------------------------------------------------------------
7 -----------
8
9 For local operation, the simplest mode of usage is:
10
11 %prog -n N
12
13 where N is the number of engines you want started.
14
15 For remote operation, you must call it with a cluster description file:
16
17 %prog -f clusterfile.py
18
19 The cluster file is a normal Python script which gets run via execfile(). You
20 can have arbitrary logic in it, but all that matters is that at the end of the
21 execution, it declares the variables 'controller', 'engines', and optionally
22 'sshx'. See the accompanying examples for details on what these variables must
23 contain.
24
25
26 Notes
27 -----
28
29 WARNING: this code is still UNFINISHED and EXPERIMENTAL! It is incomplete,
30 some listed options are not really implemented, and all of its interfaces are
31 subject to change.
32
33 When operating over SSH for a remote cluster, this program relies on the
34 existence of a particular script called 'sshx'. This script must live in the
35 target systems where you'll be running your controller and engines, and is
36 needed to configure your PATH and PYTHONPATH variables for further execution of
37 python code at the other end of an SSH connection. The script can be as simple
38 as:
39
40 #!/bin/sh
41 . $HOME/.bashrc
42 "$@"
43
44 which is the default one provided by IPython. You can modify this or provide
45 your own. Since it's quite likely that for different clusters you may need
46 this script to configure things differently or that it may live in different
47 locations, its full path can be set in the same file where you define the
48 cluster setup. IPython's order of evaluation for this variable is the
49 following:
50
51 a) Internal default: 'sshx'. This only works if it is in the default system
52 path which SSH sets up in non-interactive mode.
53
54 b) Environment variable: if $IPYTHON_SSHX is defined, this overrides the
55 internal default.
56
57 c) Variable 'sshx' in the cluster configuration file: finally, this will
58 override the previous two values.
59
60 This code is Unix-only, with precious little hope of any of this ever working
61 under Windows, since we need SSH from the ground up, we background processes,
62 etc. Ports of this functionality to Windows are welcome.
63
64
65 Call summary
66 ------------
67
68 %prog [options]
69 """
70
71 __docformat__ = "restructuredtext en"
72
73 #-------------------------------------------------------------------------------
74 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
75 #
8 #
76 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
77 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
78 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
79
12
80 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
81 # Stdlib imports
14 # Imports
82 #-------------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
83
16
84 import os
17 import os
85 import signal
18 import re
86 import sys
19 import sys
87 import time
20 import signal
21 pjoin = os.path.join
88
22
89 from optparse import OptionParser
23 from twisted.internet import reactor, defer
90 from subprocess import Popen,call
24 from twisted.internet.protocol import ProcessProtocol
25 from twisted.python import failure, log
26 from twisted.internet.error import ProcessDone, ProcessTerminated
27 from twisted.internet.utils import getProcessOutput
91
28
92 #---------------------------------------------------------------------------
29 from IPython.external import argparse
93 # IPython imports
30 from IPython.external import Itpl
94 #---------------------------------------------------------------------------
31 from IPython.kernel.twistedutil import gatherBoth
95 from IPython.tools import utils
32 from IPython.kernel.util import printer
96 from IPython.genutils import get_ipython_dir
33 from IPython.genutils import get_ipython_dir, num_cpus
97
34
98 #---------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
99 # Normal code begins
36 # General process handling code
100 #---------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
101
38
102 def parse_args():
39 def find_exe(cmd):
103 """Parse command line and return opts,args."""
40 try:
41 import win32api
42 except ImportError:
43 raise ImportError('you need to have pywin32 installed for this to work')
44 else:
45 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd)
46 return path
104
47
105 parser = OptionParser(usage=__doc__)
48 class ProcessStateError(Exception):
106 newopt = parser.add_option # shorthand
49 pass
107
50
108 newopt("--controller-port", type="int", dest="controllerport",
51 class UnknownStatus(Exception):
109 help="the TCP port the controller is listening on")
52 pass
110
53
111 newopt("--controller-ip", type="string", dest="controllerip",
54 class LauncherProcessProtocol(ProcessProtocol):
112 help="the TCP ip address of the controller")
55 """
56 A ProcessProtocol to go with the ProcessLauncher.
57 """
58 def __init__(self, process_launcher):
59 self.process_launcher = process_launcher
60
61 def connectionMade(self):
62 self.process_launcher.fire_start_deferred(self.transport.pid)
63
64 def processEnded(self, status):
65 value = status.value
66 if isinstance(value, ProcessDone):
67 self.process_launcher.fire_stop_deferred(0)
68 elif isinstance(value, ProcessTerminated):
69 self.process_launcher.fire_stop_deferred(
70 {'exit_code':value.exitCode,
71 'signal':value.signal,
72 'status':value.status
73 }
74 )
75 else:
76 raise UnknownStatus("unknown exit status, this is probably a bug in Twisted")
113
77
114 newopt("-n", "--num", type="int", dest="n",default=2,
78 def outReceived(self, data):
115 help="the number of engines to start")
79 log.msg(data)
116
80
117 newopt("--engine-port", type="int", dest="engineport",
81 def errReceived(self, data):
118 help="the TCP port the controller will listen on for engine "
82 log.err(data)
119 "connections")
120
121 newopt("--engine-ip", type="string", dest="engineip",
122 help="the TCP ip address the controller will listen on "
123 "for engine connections")
124
83
125 newopt("--mpi", type="string", dest="mpi",
84 class ProcessLauncher(object):
126 help="use mpi with package: for instance --mpi=mpi4py")
85 """
86 Start and stop an external process in an asynchronous manner.
87
88 Currently this uses deferreds to notify other parties of process state
89 changes. This is an awkward design and should be moved to using
90 a formal NotificationCenter.
91 """
92 def __init__(self, cmd_and_args):
93 self.cmd = cmd_and_args[0]
94 self.args = cmd_and_args
95 self._reset()
96
97 def _reset(self):
98 self.process_protocol = None
99 self.pid = None
100 self.start_deferred = None
101 self.stop_deferreds = []
102 self.state = 'before' # before, running, or after
103
104 @property
105 def running(self):
106 if self.state == 'running':
107 return True
108 else:
109 return False
110
111 def fire_start_deferred(self, pid):
112 self.pid = pid
113 self.state = 'running'
114 log.msg('Process %r has started with pid=%i' % (self.args, pid))
115 self.start_deferred.callback(pid)
116
117 def start(self):
118 if self.state == 'before':
119 self.process_protocol = LauncherProcessProtocol(self)
120 self.start_deferred = defer.Deferred()
121 self.process_transport = reactor.spawnProcess(
122 self.process_protocol,
123 self.cmd,
124 self.args,
125 env=os.environ
126 )
127 return self.start_deferred
128 else:
129 s = 'the process has already been started and has state: %r' % \
130 self.state
131 return defer.fail(ProcessStateError(s))
132
133 def get_stop_deferred(self):
134 if self.state == 'running' or self.state == 'before':
135 d = defer.Deferred()
136 self.stop_deferreds.append(d)
137 return d
138 else:
139 s = 'this process is already complete'
140 return defer.fail(ProcessStateError(s))
141
142 def fire_stop_deferred(self, exit_code):
143 log.msg('Process %r has stopped with %r' % (self.args, exit_code))
144 self.state = 'after'
145 for d in self.stop_deferreds:
146 d.callback(exit_code)
147
148 def signal(self, sig):
149 """
150 Send a signal to the process.
151
152 The argument sig can be ('KILL','INT', etc.) or any signal number.
153 """
154 if self.state == 'running':
155 self.process_transport.signalProcess(sig)
127
156
128 newopt("-l", "--logfile", type="string", dest="logfile",
157 # def __del__(self):
129 help="log file name")
158 # self.signal('KILL')
159
160 def interrupt_then_kill(self, delay=1.0):
161 self.signal('INT')
162 reactor.callLater(delay, self.signal, 'KILL')
163
130
164
131 newopt('-f','--cluster-file',dest='clusterfile',
165 #-----------------------------------------------------------------------------
132 help='file describing a remote cluster')
166 # Code for launching controller and engines
167 #-----------------------------------------------------------------------------
133
168
134 return parser.parse_args()
135
169
136 def numAlive(controller,engines):
170 class ControllerLauncher(ProcessLauncher):
137 """Return the number of processes still alive."""
171
138 retcodes = [controller.poll()] + \
172 def __init__(self, extra_args=None):
139 [e.poll() for e in engines]
173 if sys.platform == 'win32':
140 return retcodes.count(None)
174 args = [find_exe('ipcontroller.bat')]
175 else:
176 args = ['ipcontroller']
177 self.extra_args = extra_args
178 if extra_args is not None:
179 args.extend(extra_args)
180
181 ProcessLauncher.__init__(self, args)
141
182
142 stop = lambda pid: os.kill(pid,signal.SIGINT)
143 kill = lambda pid: os.kill(pid,signal.SIGTERM)
144
183
145 def cleanup(clean,controller,engines):
184 class EngineLauncher(ProcessLauncher):
146 """Stop the controller and engines with the given cleanup method."""
147
185
148 for e in engines:
186 def __init__(self, extra_args=None):
149 if e.poll() is None:
187 if sys.platform == 'win32':
150 print 'Stopping engine, pid',e.pid
188 args = [find_exe('ipengine.bat')]
151 clean(e.pid)
189 else:
152 if controller.poll() is None:
190 args = ['ipengine']
153 print 'Stopping controller, pid',controller.pid
191 self.extra_args = extra_args
154 clean(controller.pid)
192 if extra_args is not None:
155
193 args.extend(extra_args)
156
194
157 def ensureDir(path):
195 ProcessLauncher.__init__(self, args)
158 """Ensure a directory exists or raise an exception."""
159 if not os.path.isdir(path):
160 os.makedirs(path)
161
162
163 def startMsg(control_host,control_port=10105):
164 """Print a startup message"""
165 print
166 print 'Your cluster is up and running.'
167 print
168 print 'For interactive use, you can make a MultiEngineClient with:'
169 print
170 print 'from IPython.kernel import client'
171 print "mec = client.MultiEngineClient()"
172 print
173 print 'You can then cleanly stop the cluster from IPython using:'
174 print
175 print 'mec.kill(controller=True)'
176 print
177
196
197
198 class LocalEngineSet(object):
178
199
179 def clusterLocal(opt,arg):
200 def __init__(self, extra_args=None):
180 """Start a cluster on the local machine."""
201 self.extra_args = extra_args
202 self.launchers = []
181
203
182 # Store all logs inside the ipython directory
204 def start(self, n):
183 ipdir = get_ipython_dir()
205 dlist = []
184 pjoin = os.path.join
206 for i in range(n):
185
207 el = EngineLauncher(extra_args=self.extra_args)
186 logfile = opt.logfile
208 d = el.start()
187 if logfile is None:
209 self.launchers.append(el)
188 logdir_base = pjoin(ipdir,'log')
210 dlist.append(d)
189 ensureDir(logdir_base)
211 dfinal = gatherBoth(dlist, consumeErrors=True)
190 logfile = pjoin(logdir_base,'ipcluster-')
212 dfinal.addCallback(self._handle_start)
191
213 return dfinal
192 print 'Starting controller:',
193 controller = Popen(['ipcontroller','--logfile',logfile,'-x','-y'])
194 print 'Controller PID:',controller.pid
195
196 print 'Starting engines: ',
197 time.sleep(5)
198
199 englogfile = '%s%s-' % (logfile,controller.pid)
200 mpi = opt.mpi
201 if mpi: # start with mpi - killing the engines with sigterm will not work if you do this
202 engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi',
203 mpi, '--logfile',englogfile])]
204 # engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', mpi])]
205 else: # do what we would normally do
206 engines = [ Popen(['ipengine','--logfile',englogfile])
207 for i in range(opt.n) ]
208 eids = [e.pid for e in engines]
209 print 'Engines PIDs: ',eids
210 print 'Log files: %s*' % englogfile
211
214
212 proc_ids = eids + [controller.pid]
215 def _handle_start(self, r):
213 procs = engines + [controller]
216 log.msg('Engines started with pids: %r' % r)
214
217 return r
215 grpid = os.getpgrp()
216 try:
217 startMsg('127.0.0.1')
218 print 'You can also hit Ctrl-C to stop it, or use from the cmd line:'
219 print
220 print 'kill -INT',grpid
221 print
222 try:
223 while True:
224 time.sleep(5)
225 except:
226 pass
227 finally:
228 print 'Stopping cluster. Cleaning up...'
229 cleanup(stop,controller,engines)
230 for i in range(4):
231 time.sleep(i+2)
232 nZombies = numAlive(controller,engines)
233 if nZombies== 0:
234 print 'OK: All processes cleaned up.'
235 break
236 print 'Trying again, %d processes did not stop...' % nZombies
237 cleanup(kill,controller,engines)
238 if numAlive(controller,engines) == 0:
239 print 'OK: All processes cleaned up.'
240 break
241 else:
242 print '*'*75
243 print 'ERROR: could not kill some processes, try to do it',
244 print 'manually.'
245 zombies = []
246 if controller.returncode is None:
247 print 'Controller is alive: pid =',controller.pid
248 zombies.append(controller.pid)
249 liveEngines = [ e for e in engines if e.returncode is None ]
250 for e in liveEngines:
251 print 'Engine is alive: pid =',e.pid
252 zombies.append(e.pid)
253 print
254 print 'Zombie summary:',' '.join(map(str,zombies))
255
256 def clusterRemote(opt,arg):
257 """Start a remote cluster over SSH"""
258
259 # B. Granger, 9/3/08
260 # The launching of a remote cluster using SSH and a clusterfile
261 # is broken. Because it won't be fixed before the 0.9 release,
262 # we are removing it. For now, we just print a message to the
263 # user and abort.
264
218
265 print """The launching of a remote IPython cluster using SSL
219 def _handle_stop(self, r):
266 and a clusterfile has been removed in this release.
220 log.msg('Engines received signal: %r' % r)
267 It has been broken for a while and we are in the process
221 return r
268 of building a new process management system that will be
222
269 used to provide a more robust way of starting an IPython
223 def signal(self, sig):
270 cluster.
224 dlist = []
271
225 for el in self.launchers:
272 For now remote clusters have to be launched using ipcontroller
226 d = el.get_stop_deferred()
273 and ipengine separately.
227 dlist.append(d)
274 """
228 el.signal(sig)
275 sys.exit(1)
229 dfinal = gatherBoth(dlist, consumeErrors=True)
276
230 dfinal.addCallback(self._handle_stop)
277 # Load the remote cluster configuration
231 return dfinal
278 clConfig = {}
232
279 execfile(opt.clusterfile,clConfig)
233 def interrupt_then_kill(self, delay=1.0):
280 contConfig = clConfig['controller']
234 dlist = []
281 engConfig = clConfig['engines']
235 for el in self.launchers:
282 # Determine where to find sshx:
236 d = el.get_stop_deferred()
283 sshx = clConfig.get('sshx',os.environ.get('IPYTHON_SSHX','sshx'))
237 dlist.append(d)
238 el.interrupt_then_kill(delay)
239 dfinal = gatherBoth(dlist, consumeErrors=True)
240 dfinal.addCallback(self._handle_stop)
241 return dfinal
242
243
244 class BatchEngineSet(object):
284
245
285 # Store all logs inside the ipython directory
246 # Subclasses must fill these in. See PBSEngineSet
286 ipdir = get_ipython_dir()
247 submit_command = ''
287 pjoin = os.path.join
248 delete_command = ''
288
249 job_id_regexp = ''
289 logfile = opt.logfile
290 if logfile is None:
291 logdir_base = pjoin(ipdir,'log')
292 ensureDir(logdir_base)
293 logfile = pjoin(logdir_base,'ipcluster')
294
295 # Append this script's PID to the logfile name always
296 logfile = '%s-%s' % (logfile,os.getpid())
297
250
298 print 'Starting controller:'
251 def __init__(self, template_file, **kwargs):
299 # Controller data:
252 self.template_file = template_file
300 xsys = os.system
253 self.context = {}
301
254 self.context.update(kwargs)
302 contHost = contConfig['host']
255 self.batch_file = self.template_file+'-run'
303 contLog = '%s-con-%s-' % (logfile,contHost)
256
304 cmd = "ssh %s '%s' 'ipcontroller --logfile %s' &" % \
257 def parse_job_id(self, output):
305 (contHost,sshx,contLog)
258 m = re.match(self.job_id_regexp, output)
306 #print 'cmd:<%s>' % cmd # dbg
259 if m is not None:
307 xsys(cmd)
260 job_id = m.group()
308 time.sleep(2)
309
310 print 'Starting engines: '
311 for engineHost,engineData in engConfig.iteritems():
312 if isinstance(engineData,int):
313 numEngines = engineData
314 else:
261 else:
315 raise NotImplementedError('port configuration not finished for engines')
262 raise Exception("job id couldn't be determined: %s" % output)
316
263 self.job_id = job_id
317 print 'Sarting %d engines on %s' % (numEngines,engineHost)
264 log.msg('Job started with job id: %r' % job_id)
318 engLog = '%s-eng-%s-' % (logfile,engineHost)
265 return job_id
319 for i in range(numEngines):
266
320 cmd = "ssh %s '%s' 'ipengine --controller-ip %s --logfile %s' &" % \
267 def write_batch_script(self, n):
321 (engineHost,sshx,contHost,engLog)
268 self.context['n'] = n
322 #print 'cmd:<%s>' % cmd # dbg
269 template = open(self.template_file, 'r').read()
323 xsys(cmd)
270 log.msg('Using template for batch script: %s' % self.template_file)
324 # Wait after each host a little bit
271 script_as_string = Itpl.itplns(template, self.context)
325 time.sleep(1)
272 log.msg('Writing instantiated batch script: %s' % self.batch_file)
326
273 f = open(self.batch_file,'w')
327 startMsg(contConfig['host'])
274 f.write(script_as_string)
275 f.close()
276
277 def handle_error(self, f):
278 f.printTraceback()
279 f.raiseException()
280
281 def start(self, n):
282 self.write_batch_script(n)
283 d = getProcessOutput(self.submit_command,
284 [self.batch_file],env=os.environ)
285 d.addCallback(self.parse_job_id)
286 d.addErrback(self.handle_error)
287 return d
328
288
329 def main():
289 def kill(self):
330 """Main driver for the two big options: local or remote cluster."""
290 d = getProcessOutput(self.delete_command,
291 [self.job_id],env=os.environ)
292 return d
293
294 class PBSEngineSet(BatchEngineSet):
331
295
332 if sys.platform=='win32':
296 submit_command = 'qsub'
333 print """ipcluster does not work on Microsoft Windows. Please start
297 delete_command = 'qdel'
334 your IPython cluster using the ipcontroller and ipengine scripts."""
298 job_id_regexp = '\d+'
335 sys.exit(1)
336
299
337 opt,arg = parse_args()
300 def __init__(self, template_file, **kwargs):
301 BatchEngineSet.__init__(self, template_file, **kwargs)
302
303
304 #-----------------------------------------------------------------------------
305 # Main functions for the different types of clusters
306 #-----------------------------------------------------------------------------
307
308 # TODO:
309 # The logic in these codes should be moved into classes like LocalCluster
310 # MpirunCluster, PBSCluster, etc. This would remove alot of the duplications.
311 # The main functions should then just parse the command line arguments, create
312 # the appropriate class and call a 'start' method.
313
314 def main_local(args):
315 cont_args = []
316 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
317 if args.x:
318 cont_args.append('-x')
319 if args.y:
320 cont_args.append('-y')
321 cl = ControllerLauncher(extra_args=cont_args)
322 dstart = cl.start()
323 def start_engines(cont_pid):
324 engine_args = []
325 engine_args.append('--logfile=%s' % \
326 pjoin(args.logdir,'ipengine%s-' % cont_pid))
327 eset = LocalEngineSet(extra_args=engine_args)
328 def shutdown(signum, frame):
329 log.msg('Stopping local cluster')
330 # We are still playing with the times here, but these seem
331 # to be reliable in allowing everything to exit cleanly.
332 eset.interrupt_then_kill(0.5)
333 cl.interrupt_then_kill(0.5)
334 reactor.callLater(1.0, reactor.stop)
335 signal.signal(signal.SIGINT,shutdown)
336 d = eset.start(args.n)
337 return d
338 def delay_start(cont_pid):
339 # This is needed because the controller doesn't start listening
340 # right when it starts and the controller needs to write
341 # furl files for the engine to pick up
342 reactor.callLater(1.0, start_engines, cont_pid)
343 dstart.addCallback(delay_start)
344 dstart.addErrback(lambda f: f.raiseException())
345
346 def main_mpirun(args):
347 cont_args = []
348 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
349 if args.x:
350 cont_args.append('-x')
351 if args.y:
352 cont_args.append('-y')
353 cl = ControllerLauncher(extra_args=cont_args)
354 dstart = cl.start()
355 def start_engines(cont_pid):
356 raw_args = ['mpirun']
357 raw_args.extend(['-n',str(args.n)])
358 raw_args.append('ipengine')
359 raw_args.append('-l')
360 raw_args.append(pjoin(args.logdir,'ipengine%s-' % cont_pid))
361 if args.mpi:
362 raw_args.append('--mpi=%s' % args.mpi)
363 eset = ProcessLauncher(raw_args)
364 def shutdown(signum, frame):
365 log.msg('Stopping local cluster')
366 # We are still playing with the times here, but these seem
367 # to be reliable in allowing everything to exit cleanly.
368 eset.interrupt_then_kill(1.0)
369 cl.interrupt_then_kill(1.0)
370 reactor.callLater(2.0, reactor.stop)
371 signal.signal(signal.SIGINT,shutdown)
372 d = eset.start()
373 return d
374 def delay_start(cont_pid):
375 # This is needed because the controller doesn't start listening
376 # right when it starts and the controller needs to write
377 # furl files for the engine to pick up
378 reactor.callLater(1.0, start_engines, cont_pid)
379 dstart.addCallback(delay_start)
380 dstart.addErrback(lambda f: f.raiseException())
381
382 def main_pbs(args):
383 cont_args = []
384 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
385 if args.x:
386 cont_args.append('-x')
387 if args.y:
388 cont_args.append('-y')
389 cl = ControllerLauncher(extra_args=cont_args)
390 dstart = cl.start()
391 def start_engines(r):
392 pbs_set = PBSEngineSet(args.pbsscript)
393 def shutdown(signum, frame):
394 log.msg('Stopping pbs cluster')
395 d = pbs_set.kill()
396 d.addBoth(lambda _: cl.interrupt_then_kill(1.0))
397 d.addBoth(lambda _: reactor.callLater(2.0, reactor.stop))
398 signal.signal(signal.SIGINT,shutdown)
399 d = pbs_set.start(args.n)
400 return d
401 dstart.addCallback(start_engines)
402 dstart.addErrback(lambda f: f.raiseException())
403
404
405 def get_args():
406 base_parser = argparse.ArgumentParser(add_help=False)
407 base_parser.add_argument(
408 '-x',
409 action='store_true',
410 dest='x',
411 help='turn off client security'
412 )
413 base_parser.add_argument(
414 '-y',
415 action='store_true',
416 dest='y',
417 help='turn off engine security'
418 )
419 base_parser.add_argument(
420 "--logdir",
421 type=str,
422 dest="logdir",
423 help="directory to put log files (default=$IPYTHONDIR/log)",
424 default=pjoin(get_ipython_dir(),'log')
425 )
426 base_parser.add_argument(
427 "-n",
428 "--num",
429 type=int,
430 dest="n",
431 default=2,
432 help="the number of engines to start"
433 )
434
435 parser = argparse.ArgumentParser(
436 description='IPython cluster startup. This starts a controller and\
437 engines using various approaches. THIS IS A TECHNOLOGY PREVIEW AND\
438 THE API WILL CHANGE SIGNIFICANTLY BEFORE THE FINAL RELEASE.'
439 )
440 subparsers = parser.add_subparsers(
441 help='available cluster types. For help, do "ipcluster TYPE --help"')
442
443 parser_local = subparsers.add_parser(
444 'local',
445 help='run a local cluster',
446 parents=[base_parser]
447 )
448 parser_local.set_defaults(func=main_local)
449
450 parser_mpirun = subparsers.add_parser(
451 'mpirun',
452 help='run a cluster using mpirun',
453 parents=[base_parser]
454 )
455 parser_mpirun.add_argument(
456 "--mpi",
457 type=str,
458 dest="mpi", # Don't put a default here to allow no MPI support
459 help="how to call MPI_Init (default=mpi4py)"
460 )
461 parser_mpirun.set_defaults(func=main_mpirun)
462
463 parser_pbs = subparsers.add_parser(
464 'pbs',
465 help='run a pbs cluster',
466 parents=[base_parser]
467 )
468 parser_pbs.add_argument(
469 '--pbs-script',
470 type=str,
471 dest='pbsscript',
472 help='PBS script template',
473 default='pbs.template'
474 )
475 parser_pbs.set_defaults(func=main_pbs)
476 args = parser.parse_args()
477 return args
338
478
339 clusterfile = opt.clusterfile
479 def main():
340 if clusterfile:
480 args = get_args()
341 clusterRemote(opt,arg)
481 reactor.callWhenRunning(args.func, args)
342 else:
482 log.startLogging(sys.stdout)
343 clusterLocal(opt,arg)
483 reactor.run()
344
484
345
485 if __name__ == '__main__':
346 if __name__=='__main__':
347 main()
486 main()
@@ -64,7 +64,10 b' def make_tub(ip, port, secure, cert_file):'
64 if have_crypto:
64 if have_crypto:
65 tub = Tub(certFile=cert_file)
65 tub = Tub(certFile=cert_file)
66 else:
66 else:
67 raise SecurityError("OpenSSL is not available, so we can't run in secure mode, aborting")
67 raise SecurityError("""
68 OpenSSL/pyOpenSSL is not available, so we can't run in secure mode.
69 Try running without security using 'ipcontroller -xy'.
70 """)
68 else:
71 else:
69 tub = UnauthenticatedTub()
72 tub = UnauthenticatedTub()
70
73
@@ -202,6 +205,17 b' def start_controller():'
202 except:
205 except:
203 log.msg("Error running import_statement: %s" % cis)
206 log.msg("Error running import_statement: %s" % cis)
204
207
208 # Delete old furl files unless the reuse_furls is set
209 reuse = config['controller']['reuse_furls']
210 if not reuse:
211 paths = (config['controller']['engine_furl_file'],
212 config['controller']['controller_interfaces']['task']['furl_file'],
213 config['controller']['controller_interfaces']['multiengine']['furl_file']
214 )
215 for p in paths:
216 if os.path.isfile(p):
217 os.remove(p)
218
205 # Create the service hierarchy
219 # Create the service hierarchy
206 main_service = service.MultiService()
220 main_service = service.MultiService()
207 # The controller service
221 # The controller service
@@ -316,6 +330,12 b' def init_config():'
316 dest="ipythondir",
330 dest="ipythondir",
317 help="look for config files and profiles in this directory"
331 help="look for config files and profiles in this directory"
318 )
332 )
333 parser.add_option(
334 "-r",
335 action="store_true",
336 dest="reuse_furls",
337 help="try to reuse all furl files"
338 )
319
339
320 (options, args) = parser.parse_args()
340 (options, args) = parser.parse_args()
321
341
@@ -349,6 +369,8 b' def init_config():'
349 config['controller']['engine_tub']['cert_file'] = options.engine_cert_file
369 config['controller']['engine_tub']['cert_file'] = options.engine_cert_file
350 if options.engine_furl_file is not None:
370 if options.engine_furl_file is not None:
351 config['controller']['engine_furl_file'] = options.engine_furl_file
371 config['controller']['engine_furl_file'] = options.engine_furl_file
372 if options.reuse_furls is not None:
373 config['controller']['reuse_furls'] = options.reuse_furls
352
374
353 if options.logfile is not None:
375 if options.logfile is not None:
354 config['controller']['logfile'] = options.logfile
376 config['controller']['logfile'] = options.logfile
@@ -107,7 +107,11 b' def start_engine():'
107 furl_file = kernel_config['engine']['furl_file']
107 furl_file = kernel_config['engine']['furl_file']
108 log.msg("Using furl file: %s" % furl_file)
108 log.msg("Using furl file: %s" % furl_file)
109 d = engine_connector.connect_to_controller(engine_service, furl_file)
109 d = engine_connector.connect_to_controller(engine_service, furl_file)
110 d.addErrback(lambda _: reactor.stop())
110 def handle_error(f):
111 log.err(f)
112 if reactor.running:
113 reactor.stop()
114 d.addErrback(handle_error)
111
115
112 reactor.run()
116 reactor.run()
113
117
@@ -1,7 +1,6 b''
1 include README_Windows.txt
2 include win32_manual_post_install.py
3 include ipython.py
1 include ipython.py
4 include setupbase.py
2 include setupbase.py
3 include setupegg.py
5
4
6 graft scripts
5 graft scripts
7
6
@@ -21,31 +21,64 b" What's new"
21 6 Older releases
21 6 Older releases
22 ..
22 ..
23
23
24 Release DEV
24 Release dev
25 ===========
25 ===========
26
26
27 New features
28 ------------
29
30 * The wonderful TextMate editor can now be used with %edit on OS X. Thanks
31 to Matt Foster for this patch.
32
33 * Fully refactored :command:`ipcluster` command line program for starting
34 IPython clusters. This new version is a complete rewrite and 1) is fully
35 cross platform (we now use Twisted's process management), 2) has much
36 improved performance, 3) uses subcommands for different types of clusters,
37 4) uses argparse for parsing command line options, 5) has better support
38 for starting clusters using :command:`mpirun`, 6) has experimental support
39 for starting engines using PBS. However, this new version of ipcluster
40 should be considered a technology preview. We plan on changing the API
41 in significant ways before it is final.
42
43 * The :mod:`argparse` module has been added to :mod:`IPython.external`.
44
45 * Fully description of the security model added to the docs.
46
27 * cd completer: show bookmarks if no other completions are available.
47 * cd completer: show bookmarks if no other completions are available.
28
48
29 * Remove ipy_leo.py. "easy_install ipython-extension" to get it.
30 (done to decouple it from ipython release cycle)
31
32 * sh profile: easy way to give 'title' to prompt: assign to variable
49 * sh profile: easy way to give 'title' to prompt: assign to variable
33 '_prompt_title'. It looks like this::
50 '_prompt_title'. It looks like this::
34
51
35 [~]|1> _prompt_title = 'sudo!'
52 [~]|1> _prompt_title = 'sudo!'
36 sudo![~]|2>
53 sudo![~]|2>
37
54
55 * %edit: If you do '%edit pasted_block', pasted_block
56 variable gets updated with new data (so repeated
57 editing makes sense)
58
59 Bug fixes
60 ---------
61
62 * The ipengine and ipcontroller scripts now handle missing furl files
63 more gracefully by giving better error messages.
64
38 * %rehashx: Aliases no longer contain dots. python3.0 binary
65 * %rehashx: Aliases no longer contain dots. python3.0 binary
39 will create alias python30. Fixes:
66 will create alias python30. Fixes:
40 #259716 "commands with dots in them don't work"
67 #259716 "commands with dots in them don't work"
41
68
42 * %cpaste: %cpaste -r repeats the last pasted block.
69 * %cpaste: %cpaste -r repeats the last pasted block.
43 The block is assigned to pasted_block even if code
70 The block is assigned to pasted_block even if code
44 raises exception.
71 raises exception.
45
72
46 * %edit: If you do '%edit pasted_block', pasted_block
73 Backwards incompatible changes
47 variable gets updated with new data (so repeated
74 ------------------------------
48 editing makes sense)
75
76 * The controller now has a ``-r`` flag that needs to be used if you want to
77 reuse existing furl files. Otherwise they are deleted (the default).
78
79 * Remove ipy_leo.py. "easy_install ipython-extension" to get it.
80 (done to decouple it from ipython release cycle)
81
49
82
50
83
51 Release 0.9.1
84 Release 0.9.1
@@ -62,14 +95,14 b' Release 0.9'
62 New features
95 New features
63 ------------
96 ------------
64
97
65 * All furl files and security certificates are now put in a read-only directory
98 * All furl files and security certificates are now put in a read-only
66 named ~./ipython/security.
99 directory named ~./ipython/security.
67
100
68 * A single function :func:`get_ipython_dir`, in :mod:`IPython.genutils` that
101 * A single function :func:`get_ipython_dir`, in :mod:`IPython.genutils` that
69 determines the user's IPython directory in a robust manner.
102 determines the user's IPython directory in a robust manner.
70
103
71 * Laurent's WX application has been given a top-level script called ipython-wx,
104 * Laurent's WX application has been given a top-level script called
72 and it has received numerous fixes. We expect this code to be
105 ipython-wx, and it has received numerous fixes. We expect this code to be
73 architecturally better integrated with Gael's WX 'ipython widget' over the
106 architecturally better integrated with Gael's WX 'ipython widget' over the
74 next few releases.
107 next few releases.
75
108
@@ -80,7 +113,7 b' New features'
80 * A new, still experimental but highly functional, WX shell by Gael Varoquaux.
113 * A new, still experimental but highly functional, WX shell by Gael Varoquaux.
81 This work was sponsored by Enthought, and while it's still very new, it is
114 This work was sponsored by Enthought, and while it's still very new, it is
82 based on a more cleanly organized arhictecture of the various IPython
115 based on a more cleanly organized arhictecture of the various IPython
83 components. We will continue to develop this over the next few releases as a
116 components. We will continue to develop this over the next few releases as a
84 model for GUI components that use IPython.
117 model for GUI components that use IPython.
85
118
86 * Another GUI frontend, Cocoa based (Cocoa is the OSX native GUI framework),
119 * Another GUI frontend, Cocoa based (Cocoa is the OSX native GUI framework),
@@ -131,17 +164,17 b' New features'
131 friends have been completely refactored. Now we are checking for
164 friends have been completely refactored. Now we are checking for
132 dependencies using the approach that matplotlib uses.
165 dependencies using the approach that matplotlib uses.
133
166
134 * The documentation has been completely reorganized to accept the documentation
167 * The documentation has been completely reorganized to accept the
135 from `ipython1-dev`.
168 documentation from `ipython1-dev`.
136
169
137 * We have switched to using Foolscap for all of our network protocols in
170 * We have switched to using Foolscap for all of our network protocols in
138 :mod:`IPython.kernel`. This gives us secure connections that are both
171 :mod:`IPython.kernel`. This gives us secure connections that are both
139 encrypted and authenticated.
172 encrypted and authenticated.
140
173
141 * We have a brand new `COPYING.txt` files that describes the IPython license
174 * We have a brand new `COPYING.txt` files that describes the IPython license
142 and copyright. The biggest change is that we are putting "The IPython
175 and copyright. The biggest change is that we are putting "The IPython
143 Development Team" as the copyright holder. We give more details about
176 Development Team" as the copyright holder. We give more details about
144 exactly what this means in this file. All developer should read this and use
177 exactly what this means in this file. All developer should read this and use
145 the new banner in all IPython source code files.
178 the new banner in all IPython source code files.
146
179
147 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
180 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
@@ -3,7 +3,7 b' Configuration and customization'
3 ===============================
3 ===============================
4
4
5 .. toctree::
5 .. toctree::
6 :maxdepth: 1
6 :maxdepth: 2
7
7
8 initial_config.txt
8 initial_config.txt
9 customization.txt
9 customization.txt
@@ -8,143 +8,72 b' IPython development guidelines'
8 Overview
8 Overview
9 ========
9 ========
10
10
11 IPython is the next generation of IPython. It is named such for two reasons:
11 This document describes IPython from the perspective of developers. Most
12
12 importantly, it gives information for people who want to contribute to the
13 - Eventually, IPython will become IPython version 1.0.
13 development of IPython. So if you want to help out, read on!
14 - This new code base needs to be able to co-exist with the existing IPython until
14
15 it is a full replacement for it. Thus we needed a different name. We couldn't
15 How to contribute to IPython
16 use ``ipython`` (lowercase) as some files systems are case insensitive.
16 ============================
17
17
18 There are two, no three, main goals of the IPython effort:
18 IPython development is done using Bazaar [Bazaar]_ and Launchpad [Launchpad]_.
19
19 This makes it easy for people to contribute to the development of IPython.
20 1. Clean up the existing codebase and write lots of tests.
20 Here is a sketch of how to get going.
21 2. Separate the core functionality of IPython from the terminal to enable IPython
22 to be used from within a variety of GUI applications.
23 3. Implement a system for interactive parallel computing.
24
25 While the third goal may seem a bit unrelated to the main focus of IPython, it
26 turns out that the technologies required for this goal are nearly identical
27 with those required for goal two. This is the main reason the interactive
28 parallel computing capabilities are being put into IPython proper. Currently
29 the third of these goals is furthest along.
30
31 This document describes IPython from the perspective of developers.
32
33
34 Project organization
35 ====================
36
37 Subpackages
38 -----------
39
40 IPython is organized into semi self-contained subpackages. Each of the
41 subpackages will have its own:
42
43 - **Dependencies**. One of the most important things to keep in mind in
44 partitioning code amongst subpackages, is that they should be used to cleanly
45 encapsulate dependencies.
46
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
48 contains all of the tests for that package. For information about writing
49 tests for IPython, see the `Testing System`_ section of this document.
50
51 - **Configuration**. Each subpackage should have its own ``config``
52 subdirectory that contains the configuration information for the components
53 of the subpackage. For information about how the IPython configuration
54 system works, see the `Configuration System`_ section of this document.
55
56 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory
57 that contains all of the command line scripts associated with the subpackage.
58
59 Installation and dependencies
60 -----------------------------
61
62 IPython will not use `setuptools`_ for installation. Instead, we will use
63 standard ``setup.py`` scripts that use `distutils`_. While there are a number a
64 extremely nice features that `setuptools`_ has (like namespace packages), the
65 current implementation of `setuptools`_ has performance problems, particularly
66 on shared file systems. In particular, when Python packages are installed on
67 NSF file systems, import times become much too long (up towards 10 seconds).
68
69 Because IPython is being used extensively in the context of high performance
70 computing, where performance is critical but shared file systems are common, we
71 feel these performance hits are not acceptable. Thus, until the performance
72 problems associated with `setuptools`_ are addressed, we will stick with plain
73 `distutils`_. We are hopeful that these problems will be addressed and that we
74 will eventually begin using `setuptools`_. Because of this, we are trying to
75 organize IPython in a way that will make the eventual transition to
76 `setuptools`_ as painless as possible.
77
78 Because we will be using `distutils`_, there will be no method for
79 automatically installing dependencies. Instead, we are following the approach
80 of `Matplotlib`_ which can be summarized as follows:
81
82 - Distinguish between required and optional dependencies. However, the required
83 dependencies for IPython should be only the Python standard library.
84
85 - Upon installation check to see which optional dependencies are present and
86 tell the user which parts of IPython need which optional dependencies.
87
88 It is absolutely critical that each subpackage of IPython has a clearly
89 specified set of dependencies and that dependencies are not carelessly
90 inherited from other IPython subpackages. Furthermore, tests that have certain
91 dependencies should not fail if those dependencies are not present. Instead
92 they should be skipped and print a message.
93
94 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
95 .. _distutils: http://docs.python.org/lib/module-distutils.html
96 .. _Matplotlib: http://matplotlib.sourceforge.net/
97
98 Specific subpackages
99 --------------------
100
101 ``core``
102 This is the core functionality of IPython that is independent of the
103 terminal, network and GUIs. Most of the code that is in the current
104 IPython trunk will be refactored, cleaned up and moved here.
105
106 ``kernel``
107 The enables the IPython core to be expose to a the network. This is
108 also where all of the parallel computing capabilities are to be found.
109
110 ``config``
111 The configuration package used by IPython.
112
21
113 ``frontends``
22 Install Bazaar and create a Launchpad account
114 The various frontends for IPython. A frontend is the end-user application
23 ---------------------------------------------
115 that exposes the capabilities of IPython to the user. The most basic
116 frontend will simply be a terminal based application that looks just like
117 today 's IPython. Other frontends will likely be more powerful and based
118 on GUI toolkits.
119
24
120 ``notebook``
25 First make sure you have installed Bazaar (see their `website
121 An application that allows users to work with IPython notebooks.
26 <http://bazaar-vcs.org/>`_). To see that Bazaar is installed and knows about
27 you, try the following::
122
28
123 ``tools``
29 $ bzr whoami
124 This is where general utilities go.
30 Joe Coder <jcoder@gmail.com>
125
31
32 This should display your name and email. Next, you will want to create an
33 account on the `Launchpad website <http://www.launchpad.net>`_ and setup your
34 ssh keys. For more information of setting up your ssh keys, see `this link
35 <https://help.launchpad.net/YourAccount/CreatingAnSSHKeyPair>`_.
126
36
127 Version control
37 Get the main IPython branch from Launchpad
128 ===============
38 ------------------------------------------
129
39
130 In the past, IPython development has been done using `Subversion`__. Recently,
40 Now, you can get a copy of the main IPython development branch (we call this
131 we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it
41 the "trunk")::
132 much easier for people to contribute code to IPython. Here is a sketch of how
133 to use Bazaar for IPython development. First, you should install Bazaar.
134 After you have done that, make sure that it is working by getting the latest
135 main branch of IPython::
136
42
137 $ bzr branch lp:ipython
43 $ bzr branch lp:ipython
138
44
139 Now you can create a new branch for you to do your work in::
45 Create a working branch
46 -----------------------
47
48 When working on IPython, you won't actually make edits directly to the
49 :file:`lp:ipython` branch. Instead, you will create a separate branch for your
50 changes. For now, let's assume you want to do your work in a branch named
51 "ipython-mybranch". Create this branch by doing::
140
52
141 $ bzr branch ipython ipython-mybranch
53 $ bzr branch ipython ipython-mybranch
142
54
143 The typical work cycle in this branch will be to make changes in
55 When you actually create a branch, you will want to give it a name that
144 ``ipython-mybranch`` and then commit those changes using the commit command::
56 reflects the nature of the work that you will be doing in it, like
57 "install-docs-update".
58
59 Make edits in your working branch
60 ---------------------------------
61
62 Now you are ready to actually make edits in your :file:`ipython-mybranch`
63 branch. Before doing this, it is helpful to install this branch so you can
64 test your changes as you work. This is easiest if you have setuptools
65 installed. Then, just do::
66
67 $ cd ipython-mybranch
68 $ python setupegg.py develop
69
70 Now, make some changes. After a while, you will want to commit your changes.
71 This let's Bazaar know that you like the changes you have made and gives you
72 an opportunity to keep a nice record of what you have done. This looks like
73 this::
145
74
146 $ ...do work in ipython-mybranch...
75 $ ...do work in ipython-mybranch...
147 $ bzr ci -m "the commit message goes here"
76 $ bzr commit -m "the commit message goes here"
148
77
149 Please note that since we now don't use an old-style linear ChangeLog (that
78 Please note that since we now don't use an old-style linear ChangeLog (that
150 tends to cause problems with distributed version control systems), you should
79 tends to cause problems with distributed version control systems), you should
@@ -154,48 +83,60 b' approach in the commit messages (including the second line being left'
154
83
155 Single line summary of changes being committed.
84 Single line summary of changes being committed.
156
85
157 - more details when warranted ...
86 * more details when warranted ...
158 - including crediting outside contributors if they sent the
87 * including crediting outside contributors if they sent the
159 code/bug/idea!
88 code/bug/idea!
160
89
161 If we couple this with a policy of making single commits for each reasonably
90 As you work, you will repeat this edit/commit cycle many times. If you work on
162 atomic change, the bzr log should give an excellent view of the project, and
91 your branch for a long time, you will also want to get the latest changes from
163 the `--short` log option becomes a nice summary.
92 the :file:`lp:ipython` branch. This can be done with the following sequence of
93 commands::
164
94
165 While working with this branch, it is a good idea to merge in changes that have
95 $ ls
166 been made upstream in the parent branch. This can be done by doing::
96 ipython
167
97 ipython-mybranch
168 $ bzr pull
169
98
170 If this command shows that the branches have diverged, then you should do a
99 $ cd ipython
171 merge instead::
100 $ bzr pull
101 $ cd ../ipython-mybranch
102 $ bzr merge ../ipython
103 $ bzr commit -m "Merging changes from trunk"
172
104
173 $ bzr merge lp:ipython
105 Along the way, you should also run the IPython test suite. You can do this using the :command:`iptest` command::
174
106
175 If you want others to be able to see your branch, you can create an account
107 $ cd
176 with launchpad and push the branch to your own workspace::
108 $ iptest
177
109
178 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
110 The :command:`iptest` command will also pick up and run any tests you have written.
179
111
180 Finally, once the work in your branch is done, you can merge your changes back
112 Post your branch and request a code review
181 into the `ipython` branch by using merge::
113 ------------------------------------------
182
114
183 $ cd ipython
115 Once you are done with your edits, you should post your branch on Launchpad so
184 $ merge ../ipython-mybranch
116 that other IPython developers can review the changes and help you merge your
185 [resolve any conflicts]
117 changes into the main development branch. To post your branch on Launchpad,
186 $ bzr ci -m "Fixing that bug"
118 do::
187 $ bzr push
119
120 $ cd ipython-mybranch
121 $ bzr push lp:~yourusername/ipython/ipython-mybranch
122
123 Then, go to the `IPython Launchpad site <www.launchpad.net/ipython>`_, and you
124 should see your branch under the "Code" tab. If you click on your branch, you
125 can provide a short description of the branch as well as mark its status. Most
126 importantly, you should click the link that reads "Propose for merging into
127 another branch". What does this do?
188
128
189 But this will require you to have write permissions to the `ipython` branch.
129 This let's the other IPython developers know that your branch is ready to be
190 It you don't you can tell one of the IPython devs about your branch and they
130 reviewed and merged into the main development branch. During this review
191 can do the merge for you.
131 process, other developers will give you feedback and help you get your code
132 ready to be merged. What types of things will we be looking for:
192
133
193 More information about Bazaar workflows can be found `here`__.
134 * All code is documented.
135 * All code has tests.
136 * The entire IPython test suite passes.
194
137
195 .. __: http://subversion.tigris.org/
138 Once your changes have been reviewed and approved, someone will merge them
196 .. __: http://bazaar-vcs.org/
139 into the main development branch.
197 .. __: http://www.launchpad.net/ipython
198 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
199
140
200 Documentation
141 Documentation
201 =============
142 =============
@@ -204,39 +145,31 b' Standalone documentation'
204 ------------------------
145 ------------------------
205
146
206 All standalone documentation should be written in plain text (``.txt``) files
147 All standalone documentation should be written in plain text (``.txt``) files
207 using `reStructuredText`_ for markup and formatting. All such documentation
148 using reStructuredText [reStructuredText]_ for markup and formatting. All such
208 should be placed in the top level directory ``docs`` of the IPython source
149 documentation should be placed in directory :file:`docs/source` of the IPython
209 tree. Or, when appropriate, a suitably named subdirectory should be used. The
150 source tree. The documentation in this location will serve as the main source
210 documentation in this location will serve as the main source for IPython
151 for IPython documentation and all existing documentation should be converted
211 documentation and all existing documentation should be converted to this
152 to this format.
212 format.
213
153
214 In the future, the text files in the ``docs`` directory will be used to
154 To build the final documentation, we use Sphinx [Sphinx]_. Once you have Sphinx installed, you can build the html docs yourself by doing::
215 generate all forms of documentation for IPython. This include documentation on
216 the IPython website as well as *pdf* documentation.
217
155
218 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
156 $ cd ipython-mybranch/docs
157 $ make html
219
158
220 Docstring format
159 Docstring format
221 ----------------
160 ----------------
222
161
223 Good docstrings are very important. All new code will use `Epydoc`_ for
162 Good docstrings are very important. All new code should have docstrings that
224 generating API docs, so we will follow the `Epydoc`_ conventions. More
163 are formatted using reStructuredText for markup and formatting, since it is
225 specifically, we will use `reStructuredText`_ for markup and formatting, since
164 understood by a wide variety of tools. Details about using reStructuredText
226 it is understood by a wide variety of tools. This means that if in the future
165 for docstrings can be found `here
227 we have any reason to change from `Epydoc`_ to something else, we'll have fewer
228 transition pains.
229
230 Details about using `reStructuredText`_ for docstrings can be found `here
231 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
166 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
232
167
233 .. _Epydoc: http://epydoc.sourceforge.net/
234
235 Additional PEPs of interest regarding documentation of code:
168 Additional PEPs of interest regarding documentation of code:
236
169
237 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
170 * `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
238 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
171 * `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
239 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
172 * `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
240
173
241
174
242 Coding conventions
175 Coding conventions
@@ -248,17 +181,16 b' General'
248 In general, we'll try to follow the standard Python style conventions as
181 In general, we'll try to follow the standard Python style conventions as
249 described here:
182 described here:
250
183
251 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
184 * `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
252
185
253
186
254 Other comments:
187 Other comments:
255
188
256 - In a large file, top level classes and functions should be
189 * In a large file, top level classes and functions should be
257 separated by 2-3 lines to make it easier to separate them visually.
190 separated by 2-3 lines to make it easier to separate them visually.
258 - Use 4 spaces for indentation.
191 * Use 4 spaces for indentation.
259 - Keep the ordering of methods the same in classes that have the same
192 * Keep the ordering of methods the same in classes that have the same
260 methods. This is particularly true for classes that implement
193 methods. This is particularly true for classes that implement an interface.
261 similar interfaces and for interfaces that are similar.
262
194
263 Naming conventions
195 Naming conventions
264 ------------------
196 ------------------
@@ -268,38 +200,33 b' Guide for Python Code`_.'
268
200
269 For all new IPython code (and much existing code is being refactored), we'll use:
201 For all new IPython code (and much existing code is being refactored), we'll use:
270
202
271 - All ``lowercase`` module names.
203 * All ``lowercase`` module names.
272
204
273 - ``CamelCase`` for class names.
205 * ``CamelCase`` for class names.
274
206
275 - ``lowercase_with_underscores`` for methods, functions, variables and
207 * ``lowercase_with_underscores`` for methods, functions, variables and
276 attributes.
208 attributes.
277
209
278 This may be confusing as most of the existing IPython codebase uses a different
210 There are, however, some important exceptions to these rules. In some cases,
279 convention (``lowerCamelCase`` for methods and attributes). Slowly, we will
280 move IPython over to the new convention, providing shadow names for backward
281 compatibility in public interfaces.
282
283 There are, however, some important exceptions to these rules. In some cases,
284 IPython code will interface with packages (Twisted, Wx, Qt) that use other
211 IPython code will interface with packages (Twisted, Wx, Qt) that use other
285 conventions. At some level this makes it impossible to adhere to our own
212 conventions. At some level this makes it impossible to adhere to our own
286 standards at all times. In particular, when subclassing classes that use other
213 standards at all times. In particular, when subclassing classes that use other
287 naming conventions, you must follow their naming conventions. To deal with
214 naming conventions, you must follow their naming conventions. To deal with
288 cases like this, we propose the following policy:
215 cases like this, we propose the following policy:
289
216
290 - If you are subclassing a class that uses different conventions, use its
217 * If you are subclassing a class that uses different conventions, use its
291 naming conventions throughout your subclass. Thus, if you are creating a
218 naming conventions throughout your subclass. Thus, if you are creating a
292 Twisted Protocol class, used Twisted's
219 Twisted Protocol class, used Twisted's
293 ``namingSchemeForMethodsAndAttributes.``
220 ``namingSchemeForMethodsAndAttributes.``
294
221
295 - All IPython's official interfaces should use our conventions. In some cases
222 * All IPython's official interfaces should use our conventions. In some cases
296 this will mean that you need to provide shadow names (first implement
223 this will mean that you need to provide shadow names (first implement
297 ``fooBar`` and then ``foo_bar = fooBar``). We want to avoid this at all
224 ``fooBar`` and then ``foo_bar = fooBar``). We want to avoid this at all
298 costs, but it will probably be necessary at times. But, please use this
225 costs, but it will probably be necessary at times. But, please use this
299 sparingly!
226 sparingly!
300
227
301 Implementation-specific *private* methods will use
228 Implementation-specific *private* methods will use
302 ``_single_underscore_prefix``. Names with a leading double underscore will
229 ``_single_underscore_prefix``. Names with a leading double underscore will
303 *only* be used in special cases, as they makes subclassing difficult (such
230 *only* be used in special cases, as they makes subclassing difficult (such
304 names are not easily seen by child classes).
231 names are not easily seen by child classes).
305
232
@@ -323,107 +250,38 b' specific ``IPY`` or ``ipy`` are preferred.'
323 Testing system
250 Testing system
324 ==============
251 ==============
325
252
326 It is extremely important that all code contributed to IPython has tests. Tests
253 It is extremely important that all code contributed to IPython has tests.
327 should be written as unittests, doctests or as entities that the `Nose`_
254 Tests should be written as unittests, doctests or as entities that the Nose
328 testing package will find. Regardless of how the tests are written, we will use
255 [Nose]_ testing package will find. Regardless of how the tests are written, we
329 `Nose`_ for discovering and running the tests. `Nose`_ will be required to run
256 will use Nose for discovering and running the tests. Nose will be required to
330 the IPython test suite, but will not be required to simply use IPython.
257 run the IPython test suite, but will not be required to simply use IPython.
331
258
332 .. _Nose: http://code.google.com/p/python-nose/
259 Tests of Twisted using code need to follow two additional guidelines:
333
260
334 Tests of `Twisted`__ using code should be written by subclassing the
261 1. Twisted using tests should be written by subclassing the :class:`TestCase`
335 ``TestCase`` class that comes with ``twisted.trial.unittest``. When this is
262 class that comes with :mod:`twisted.trial.unittest`.
336 done, `Nose`_ will be able to run the tests and the twisted reactor will be
337 handled correctly.
338
263
339 .. __: http://www.twistedmatrix.com
264 2. All :class:`Deferred` instances that are created in the test must be
265 properly chained and the final one *must* be the return value of the test
266 method.
340
267
341 Each subpackage in IPython should have its own ``tests`` directory that
268 When these two things are done, Nose will be able to run the tests and the
269 twisted reactor will be handled correctly.
270
271 Each subpackage in IPython should have its own :file:`tests` directory that
342 contains all of the tests for that subpackage. This allows each subpackage to
272 contains all of the tests for that subpackage. This allows each subpackage to
343 be self-contained. If a subpackage has any dependencies beyond the Python
273 be self-contained. If a subpackage has any dependencies beyond the Python
344 standard library, the tests for that subpackage should be skipped if the
274 standard library, the tests for that subpackage should be skipped if the
345 dependencies are not found. This is very important so users don't get tests
275 dependencies are not found. This is very important so users don't get tests
346 failing simply because they don't have dependencies.
276 failing simply because they don't have dependencies.
347
277
348 We also need to look into use Noses ability to tag tests to allow a more
278 To run the IPython test suite, use the :command:`iptest` command that is installed with IPython::
349 modular approach of running tests.
350
279
351 .. _devel_config:
280 $ iptest
352
281
353 Configuration system
282 This command runs Nose with the proper options and extensions.
354 ====================
355
356 IPython uses `.ini`_ files for configuration purposes. This represents a huge
357 improvement over the configuration system used in IPython. IPython works with
358 these files using the `ConfigObj`_ package, which IPython includes as
359 ``ipython1/external/configobj.py``.
360
361 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of
362 IPython should contain a ``config`` subdirectory that contains all of the
363 configuration information for the subpackage. To see how configuration
364 information is defined (along with defaults) see at the examples in
365 ``ipython1/kernel/config`` and ``ipython1/core/config``. Likewise, to see how
366 the configuration information is used, see examples in
367 ``ipython1/kernel/scripts/ipengine.py``.
368
369 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We
370 are calling this new layer, ``tconfig``, as it will use a `Traits`_-like
371 validation model. We won't actually use `Traits`_, but will implement
372 something similar in pure Python. But, even in this new system, we will still
373 use `ConfigObj`_ and `.ini`_ files underneath the hood. Talk to Fernando if you
374 are interested in working on this part of IPython. The current prototype of
375 ``tconfig`` is located in the IPython sandbox.
376
377 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
378 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
379 .. _Traits: http://code.enthought.com/traits/
380
381
382 Installation and testing scenarios
383 ==================================
384
385 This section outlines the various scenarios that we need to test before we
386 release an IPython version. These scenarios represent different ways of
387 installing IPython and its dependencies.
388
389 Installation scenarios under Linux and OS X
390 -------------------------------------------
391
392 1. Install from tarball using ``python setup.py install``.
393 a. With only readline+nose dependencies installed.
394 b. With all dependencies installed (readline, zope.interface, Twisted,
395 foolscap, Sphinx, nose, pyOpenSSL).
396
397 2. Install using easy_install.
398
399 a. With only readline+nose dependencies installed.
400 i. Default dependencies: ``easy_install ipython-0.9.beta3-py2.5.egg``
401 ii. Optional dependency sets: ``easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]``
402
403 b. With all dependencies already installed.
404
405
406 Installation scenarios under Win32
407 ----------------------------------
408
409 1. Install everything from .exe installers
410 2. easy_install?
411
412
413 Tests to run for these scenarios
414 --------------------------------
415
416 1. Run the full test suite.
417 2. Start a controller and engines and try a few things by hand.
418 a. Using ipcluster.
419 b. Using ipcontroller/ipengine by hand.
420
421 3. Run a few of the parallel examples.
422 4. Try the kernel with and without security with and without PyOpenSSL
423 installed.
424 5. Beat on the IPython terminal a bunch.
425 6. Make sure that furl files are being put in proper locations.
426
283
284 .. _devel_config:
427
285
428 Release checklist
286 Release checklist
429 =================
287 =================
@@ -434,9 +292,9 b' Most of the release process is automated by the :file:`release` script in the'
434 #. Run the release script, which makes the tar.gz, eggs and Win32 .exe
292 #. Run the release script, which makes the tar.gz, eggs and Win32 .exe
435 installer. It posts them to the site and registers the release with PyPI.
293 installer. It posts them to the site and registers the release with PyPI.
436
294
437 #. Updating the website with announcements and links to the updated changes.txt
295 #. Updating the website with announcements and links to the updated
438 in html form. Remember to put a short note both on the news page of the site
296 changes.txt in html form. Remember to put a short note both on the news
439 and on launcphad.
297 page of the site and on Launcphad.
440
298
441 #. Drafting a short release announcement with i) highlights and ii) a link to
299 #. Drafting a short release announcement with i) highlights and ii) a link to
442 the html changes.txt.
300 the html changes.txt.
@@ -444,3 +302,9 b' Most of the release process is automated by the :file:`release` script in the'
444 #. Make sure that the released version of the docs is live on the site.
302 #. Make sure that the released version of the docs is live on the site.
445
303
446 #. Celebrate!
304 #. Celebrate!
305
306 .. [Bazaar] Bazaar. http://bazaar-vcs.org/
307 .. [Launchpad] Launchpad. http://www.launchpad.net/ipython
308 .. [reStructuredText] reStructuredText. http://docutils.sourceforge.net/rst.html
309 .. [Sphinx] Sphinx. http://sphinx.pocoo.org/
310 .. [Nose] Nose: a discovery based unittest extension. http://code.google.com/p/python-nose/
@@ -8,3 +8,4 b' Development'
8 development.txt
8 development.txt
9 roadmap.txt
9 roadmap.txt
10 notification_blueprint.txt
10 notification_blueprint.txt
11 config_blueprint.txt
@@ -6,27 +6,50 b' IPython.kernel.core.notification blueprint'
6
6
7 Overview
7 Overview
8 ========
8 ========
9 The :mod:`IPython.kernel.core.notification` module will provide a simple implementation of a notification center and support for the observer pattern within the :mod:`IPython.kernel.core`. The main intended use case is to provide notification of Interpreter events to an observing frontend during the execution of a single block of code.
9
10 The :mod:`IPython.kernel.core.notification` module will provide a simple
11 implementation of a notification center and support for the observer pattern
12 within the :mod:`IPython.kernel.core`. The main intended use case is to
13 provide notification of Interpreter events to an observing frontend during the
14 execution of a single block of code.
10
15
11 Functional Requirements
16 Functional Requirements
12 =======================
17 =======================
18
13 The notification center must:
19 The notification center must:
14 * Provide synchronous notification of events to all registered observers.
20
15 * Provide typed or labeled notification types
21 * Provide synchronous notification of events to all registered observers.
16 * Allow observers to register callbacks for individual or all notification types
22
17 * Allow observers to register callbacks for events from individual or all notifying objects
23 * Provide typed or labeled notification types.
18 * Notification to the observer consists of the notification type, notifying object and user-supplied extra information [implementation: as keyword parameters to the registered callback]
24
19 * Perform as O(1) in the case of no registered observers.
25 * Allow observers to register callbacks for individual or all notification
20 * Permit out-of-process or cross-network extension.
26 types.
27
28 * Allow observers to register callbacks for events from individual or all
29 notifying objects.
30
31 * Notification to the observer consists of the notification type, notifying
32 object and user-supplied extra information [implementation: as keyword
33 parameters to the registered callback].
34
35 * Perform as O(1) in the case of no registered observers.
36
37 * Permit out-of-process or cross-network extension.
21
38
22 What's not included
39 What's not included
23 ==============================================================
40 ===================
41
24 As written, the :mod:`IPython.kernel.core.notificaiton` module does not:
42 As written, the :mod:`IPython.kernel.core.notificaiton` module does not:
25 * Provide out-of-process or network notifications [these should be handled by a separate, Twisted aware module in :mod:`IPython.kernel`].
43
26 * Provide zope.interface-style interfaces for the notification system [these should also be provided by the :mod:`IPython.kernel` module]
44 * Provide out-of-process or network notifications (these should be handled by
45 a separate, Twisted aware module in :mod:`IPython.kernel`).
46
47 * Provide zope.interface-style interfaces for the notification system (these
48 should also be provided by the :mod:`IPython.kernel` module).
27
49
28 Use Cases
50 Use Cases
29 =========
51 =========
52
30 The following use cases describe the main intended uses of the notificaiton module and illustrate the main success scenario for each use case:
53 The following use cases describe the main intended uses of the notificaiton module and illustrate the main success scenario for each use case:
31
54
32 1. Dwight Schroot is writing a frontend for the IPython project. His frontend is stuck in the stone age and must communicate synchronously with an IPython.kernel.core.Interpreter instance. Because code is executed in blocks by the Interpreter, Dwight's UI freezes every time he executes a long block of code. To keep track of the progress of his long running block, Dwight adds the following code to his frontend's set-up code::
55 1. Dwight Schroot is writing a frontend for the IPython project. His frontend is stuck in the stone age and must communicate synchronously with an IPython.kernel.core.Interpreter instance. Because code is executed in blocks by the Interpreter, Dwight's UI freezes every time he executes a long block of code. To keep track of the progress of his long running block, Dwight adds the following code to his frontend's set-up code::
@@ -40,9 +63,20 b' The following use cases describe the main intended uses of the notificaiton modu'
40 def stdout_notification(self, type, notifying_object, out_string=None):
63 def stdout_notification(self, type, notifying_object, out_string=None):
41 self.writeStdOut(out_string)
64 self.writeStdOut(out_string)
42
65
43 If everything works, the Interpreter will (according to its published API) fire a notification via the :data:`IPython.kernel.core.notification.sharedCenter` of type :const:`STD_OUT_NOTIFICATION_TYPE` before writing anything to stdout [it's up to the Intereter implementation to figure out when to do this]. The notificaiton center will then call the registered callbacks for that event type (in this case, Dwight's frontend's stdout_notification method). Again, according to its API, the Interpreter provides an additional keyword argument when firing the notificaiton of out_string, a copy of the string it will write to stdout.
66 If everything works, the Interpreter will (according to its published API)
67 fire a notification via the
68 :data:`IPython.kernel.core.notification.sharedCenter` of type
69 :const:`STD_OUT_NOTIFICATION_TYPE` before writing anything to stdout [it's up
70 to the Intereter implementation to figure out when to do this]. The
71 notificaiton center will then call the registered callbacks for that event
72 type (in this case, Dwight's frontend's stdout_notification method). Again,
73 according to its API, the Interpreter provides an additional keyword argument
74 when firing the notificaiton of out_string, a copy of the string it will write
75 to stdout.
44
76
45 Like magic, Dwight's frontend is able to provide output, even during long-running calculations. Now if Jim could just convince Dwight to use Twisted...
77 Like magic, Dwight's frontend is able to provide output, even during
78 long-running calculations. Now if Jim could just convince Dwight to use
79 Twisted...
46
80
47 2. Boss Hog is writing a frontend for the IPython project. Because Boss Hog is stuck in the stone age, his frontend will be written in a new Fortran-like dialect of python and will run only from the command line. Because he doesn't need any fancy notification system and is used to worrying about every cycle on his rat-wheel powered mini, Boss Hog is adamant that the new notification system not produce any performance penalty. As they say in Hazard county, there's no such thing as a free lunch. If he wanted zero overhead, he should have kept using IPython 0.8. Instead, those tricky Duke boys slide in a suped-up bridge-out jumpin' awkwardly confederate-lovin' notification module that imparts only a constant (and small) performance penalty when the Interpreter (or any other object) fires an event for which there are no registered observers. Of course, the same notificaiton-enabled Interpreter can then be used in frontends that require notifications, thus saving the IPython project from a nasty civil war.
81 2. Boss Hog is writing a frontend for the IPython project. Because Boss Hog is stuck in the stone age, his frontend will be written in a new Fortran-like dialect of python and will run only from the command line. Because he doesn't need any fancy notification system and is used to worrying about every cycle on his rat-wheel powered mini, Boss Hog is adamant that the new notification system not produce any performance penalty. As they say in Hazard county, there's no such thing as a free lunch. If he wanted zero overhead, he should have kept using IPython 0.8. Instead, those tricky Duke boys slide in a suped-up bridge-out jumpin' awkwardly confederate-lovin' notification module that imparts only a constant (and small) performance penalty when the Interpreter (or any other object) fires an event for which there are no registered observers. Of course, the same notificaiton-enabled Interpreter can then be used in frontends that require notifications, thus saving the IPython project from a nasty civil war.
48
82
@@ -4,104 +4,78 b''
4 Development roadmap
4 Development roadmap
5 ===================
5 ===================
6
6
7 .. contents::
8
9 IPython is an ambitious project that is still under heavy development. However, we want IPython to become useful to as many people as possible, as quickly as possible. To help us accomplish this, we are laying out a roadmap of where we are headed and what needs to happen to get there. Hopefully, this will help the IPython developers figure out the best things to work on for each upcoming release.
7 IPython is an ambitious project that is still under heavy development. However, we want IPython to become useful to as many people as possible, as quickly as possible. To help us accomplish this, we are laying out a roadmap of where we are headed and what needs to happen to get there. Hopefully, this will help the IPython developers figure out the best things to work on for each upcoming release.
10
8
11 Speaking of releases, we are going to begin releasing a new version of IPython every four weeks. We are hoping that a regular release schedule, along with a clear roadmap of where we are headed will propel the project forward.
9 Work targeted to particular releases
12
10 ====================================
13 Where are we headed
14 ===================
15
16 Our goal with IPython is simple: to provide a *powerful*, *robust* and *easy to use* framework for parallel computing. While there are other secondary goals you will hear us talking about at various times, this is the primary goal of IPython that frames the roadmap.
17
18 Steps along the way
19 ===================
20
21 Here we describe the various things that we need to work on to accomplish this goal.
22
23 Setting up for regular release schedule
24 ---------------------------------------
25
26 We would like to begin to release IPython regularly (probably a 4 week release cycle). To get ready for this, we need to revisit the development guidelines and put in information about releasing IPython.
27
11
28 Process startup and management
12 Release 0.10
29 ------------------------------
13 ------------
30
14
31 IPython is implemented using a distributed set of processes that communicate using TCP/IP network channels. Currently, users have to start each of the various processes separately using command line scripts. This is both difficult and error prone. Furthermore, there are a number of things that often need to be managed once the processes have been started, such as the sending of signals and the shutting down and cleaning up of processes.
15 * Initial refactor of :command:`ipcluster`.
32
16
33 We need to build a system that makes it trivial for users to start and manage IPython processes. This system should have the following properties:
17 * Better TextMate integration.
34
18
35 * It should possible to do everything through an extremely simple API that users
19 * Merge in the daemon branch.
36 can call from their own Python script. No shell commands should be needed.
37
38 * This simple API should be configured using standard .ini files.
39
20
40 * The system should make it possible to start processes using a number of different
21 Release 0.11
41 approaches: SSH, PBS/Torque, Xgrid, Windows Server, mpirun, etc.
22 ------------
42
43 * The controller and engine processes should each have a daemon for monitoring,
44 signaling and clean up.
45
46 * The system should be secure.
47
23
48 * The system should work under all the major operating systems, including
24 * Refactor the configuration system and command line options for
49 Windows.
25 :command:`ipengine` and :command:`ipcontroller`. This will include the
26 creation of cluster directories that encapsulate all the configuration
27 files, log files and security related files for a particular cluster.
50
28
51 Initial work has begun on the daemon infrastructure, and some of the needed logic is contained in the ipcluster script.
29 * Refactor :command:`ipcluster` to support the new configuration system.
52
30
53 Ease of use/high-level approaches to parallelism
31 * Refactor the daemon stuff to support the new configuration system.
54 ------------------------------------------------
55
32
56 While our current API for clients is well designed, we can still do a lot better in designing a user-facing API that is super simple. The main goal here is that it should take *almost no extra code* for users to get their code running in parallel. For this to be possible, we need to tie into Python's standard idioms that enable efficient coding. The biggest ones we are looking at are using context managers (i.e., Python 2.5's ``with`` statement) and decorators. Initial work on this front has begun, but more work is needed.
33 * Merge back in the core of the notebook.
57
34
58 We also need to think about new models for expressing parallelism. This is fun work as most of the foundation has already been established.
35 Release 0.12
36 ------------
59
37
60 Security
38 * Fully integrate process startup with the daemons for full process
61 --------
39 management.
62
40
63 Currently, IPython has no built in security or security model. Because we would like IPython to be usable on public computer systems and over wide area networks, we need to come up with a robust solution for security. Here are some of the specific things that need to be included:
41 * Make the capabilites of :command:`ipcluster` available from simple Python
42 classes.
64
43
65 * User authentication between all processes (engines, controller and clients).
44 Major areas of work
45 ===================
66
46
67 * Optional TSL/SSL based encryption of all communication channels.
47 Refactoring the main IPython core
48 ---------------------------------
68
49
69 * A good way of picking network ports so multiple users on the same system can
50 Process management for :mod:`IPython.kernel`
70 run their own controller and engines without interfering with those of others.
51 --------------------------------------------
71
72 * A clear model for security that enables users to evaluate the security risks
73 associated with using IPython in various manners.
74
52
75 For the implementation of this, we plan on using Twisted's support for SSL and authentication. One things that we really should look at is the `Foolscap`_ network protocol, which provides many of these things out of the box.
53 Configuration system
54 --------------------
76
55
77 .. _Foolscap: http://foolscap.lothar.com/trac
56 Performance problems
57 --------------------
78
58
79 The security work needs to be done in conjunction with other network protocol stuff.
59 Currently, we have a number of performance issues that are waiting to bite users:
80
60
81 As of the 0.9 release of IPython, we are using Foolscap and we have implemented
61 * The controller stores a large amount of state in Python dictionaries. Under
82 a full security model.
62 heavy usage, these dicts with get very large, causing memory usage problems.
63 We need to develop more scalable solutions to this problem, such as using a
64 sqlite database to store this state. This will also help the controller to
65 be more fault tolerant.
83
66
84 Latent performance issues
67 * We currently don't have a good way of handling large objects in the
85 -------------------------
68 controller. The biggest problem is that because we don't have any way of
69 streaming objects, we get lots of temporary copies in the low-level buffers.
70 We need to implement a better serialization approach and true streaming
71 support.
86
72
87 Currently, we have a number of performance issues that are waiting to bite users:
73 * The controller currently unpickles and repickles objects. We need to use the
74 [push|pull]_serialized methods instead.
88
75
89 * The controller store a large amount of state in Python dictionaries. Under heavy
76 * Currently the controller is a bottleneck. The best approach for this is to
90 usage, these dicts with get very large, causing memory usage problems. We need to
77 separate the controller itself into multiple processes, one for the core
91 develop more scalable solutions to this problem, such as using a sqlite database
78 controller and one each for the controller interfaces.
92 to store this state. This will also help the controller to be more fault tolerant.
93 * Currently, the client to controller connections are done through XML-RPC using
94 HTTP 1.0. This is very inefficient as XML-RPC is a very verbose protocol and
95 each request must be handled with a new connection. We need to move these network
96 connections over to PB or Foolscap. Done!
97 * We currently don't have a good way of handling large objects in the controller.
98 The biggest problem is that because we don't have any way of streaming objects,
99 we get lots of temporary copies in the low-level buffers. We need to implement
100 a better serialization approach and true streaming support.
101 * The controller currently unpickles and repickles objects. We need to use the
102 [push|pull]_serialized methods instead.
103 * Currently the controller is a bottleneck. We need the ability to scale the
104 controller by aggregating multiple controllers into one effective controller.
105
79
106
80
107
81
@@ -24,9 +24,7 b' IPython Documentation'
24 license_and_copyright.txt
24 license_and_copyright.txt
25 credits.txt
25 credits.txt
26
26
27
28 .. htmlonly::
27 .. htmlonly::
29
28 * :ref:`genindex`
30 * :ref:`genindex`
29 * :ref:`modindex`
31 * :ref:`modindex`
30 * :ref:`search`
32 * :ref:`search`
@@ -16,6 +16,19 b' Some of the installation approaches use the :mod:`setuptools` package and its :c'
16
16
17 More general information about installing Python packages can be found in Python's documentation at http://www.python.org/doc/.
17 More general information about installing Python packages can be found in Python's documentation at http://www.python.org/doc/.
18
18
19 Quickstart
20 ==========
21
22 If you have :mod:`setuptools` installed and you are on OS X or Linux (not Windows), the following will download and install IPython *and* the main optional dependencies::
23
24 $ easy_install ipython[kernel,security,test]
25
26 This will get Twisted, zope.interface and Foolscap, which are needed for IPython's parallel computing features as well as the nose package, which will enable you to run IPython's test suite. To run IPython's test suite, use the :command:`iptest` command::
27
28 $ iptest
29
30 Read on for more specific details and instructions for Windows.
31
19 Installing IPython itself
32 Installing IPython itself
20 =========================
33 =========================
21
34
@@ -26,7 +39,7 b' Installation using easy_install'
26
39
27 If you have :mod:`setuptools` installed, the easiest way of getting IPython is to simple use :command:`easy_install`::
40 If you have :mod:`setuptools` installed, the easiest way of getting IPython is to simple use :command:`easy_install`::
28
41
29 $ easy_install IPython
42 $ easy_install ipython
30
43
31 That's it.
44 That's it.
32
45
@@ -106,7 +119,13 b' Most users on OS X will want to get the full :mod:`readline` module. To get a w'
106
119
107 If needed, the readline egg can be build and installed from source (see the wiki page at http://ipython.scipy.org/moin/InstallationOSXLeopard).
120 If needed, the readline egg can be build and installed from source (see the wiki page at http://ipython.scipy.org/moin/InstallationOSXLeopard).
108
121
109 On Windows, you will need the PyReadline module. PyReadline is a separate, Windows only implementation of readline that uses native Windows calls through :mod:`ctypes`. The easiest way of installing PyReadline is you use the binary installer available `here <http://ipython.scipy.org/dist/>`_. The :mod:`ctypes` module, which comes with Python 2.5 and greater, is required by PyReadline. It is available for Python 2.4 at http://python.net/crew/theller/ctypes.
122 On Windows, you will need the PyReadline module. PyReadline is a separate,
123 Windows only implementation of readline that uses native Windows calls through
124 :mod:`ctypes`. The easiest way of installing PyReadline is you use the binary
125 installer available `here <http://ipython.scipy.org/dist/>`_. The
126 :mod:`ctypes` module, which comes with Python 2.5 and greater, is required by
127 PyReadline. It is available for Python 2.4 at
128 http://python.net/crew/theller/ctypes.
110
129
111 nose
130 nose
112 ----
131 ----
@@ -117,7 +136,7 b' To run the IPython test suite you will need the :mod:`nose` package. Nose provi'
117
136
118 Another way of getting this is to do::
137 Another way of getting this is to do::
119
138
120 $ easy_install IPython[test]
139 $ easy_install ipython[test]
121
140
122 For more installation options, see the `nose website <http://somethingaboutorange.com/mrl/projects/nose/>`_. Once you have nose installed, you can run IPython's test suite using the iptest command::
141 For more installation options, see the `nose website <http://somethingaboutorange.com/mrl/projects/nose/>`_. Once you have nose installed, you can run IPython's test suite using the iptest command::
123
142
@@ -145,13 +164,15 b' The IPython kernel provides a nice architecture for parallel computing. The mai'
145
164
146 On a Unix style platform (including OS X), if you want to use :mod:`setuptools`, you can just do::
165 On a Unix style platform (including OS X), if you want to use :mod:`setuptools`, you can just do::
147
166
148 $ easy_install IPython[kernel] # the first three
167 $ easy_install ipython[kernel] # the first three
149 $ easy_install IPython[security] # pyOpenSSL
168 $ easy_install ipython[security] # pyOpenSSL
150
169
151 zope.interface and Twisted
170 zope.interface and Twisted
152 --------------------------
171 --------------------------
153
172
154 On Unix style platforms (including OS X), the simplest way of getting the these is to use :command:`easy_install`::
173 Twisted [Twisted]_ and zope.interface [ZopeInterface]_ are used for networking related things. On Unix
174 style platforms (including OS X), the simplest way of getting the these is to
175 use :command:`easy_install`::
155
176
156 $ easy_install zope.interface
177 $ easy_install zope.interface
157 $ easy_install Twisted
178 $ easy_install Twisted
@@ -163,7 +184,7 b' Windows is a bit different. For zope.interface and Twisted, simply get the late'
163 Foolscap
184 Foolscap
164 --------
185 --------
165
186
166 Foolscap uses Twisted to provide a very nice secure RPC protocol that we use to implement our parallel computing features.
187 Foolscap [Foolscap]_ uses Twisted to provide a very nice secure RPC protocol that we use to implement our parallel computing features.
167
188
168 On all platforms a simple::
189 On all platforms a simple::
169
190
@@ -174,7 +195,7 b' should work. You can also download the source tarballs from the `Foolscap websi'
174 pyOpenSSL
195 pyOpenSSL
175 ---------
196 ---------
176
197
177 IPython requires an older version of pyOpenSSL (0.6 rather than the current 0.7). There are a couple of options for getting this:
198 IPython requires an older version of pyOpenSSL [pyOpenSSL]_ (0.6 rather than the current 0.7). There are a couple of options for getting this:
178
199
179 1. Most Linux distributions have packages for pyOpenSSL.
200 1. Most Linux distributions have packages for pyOpenSSL.
180 2. The built-in Python 2.5 on OS X 10.5 already has it installed.
201 2. The built-in Python 2.5 on OS X 10.5 already has it installed.
@@ -188,4 +209,9 b' Dependencies for IPython.frontend (the IPython GUI)'
188 wxPython
209 wxPython
189 --------
210 --------
190
211
191 Starting with IPython 0.9, IPython has a new IPython.frontend package that has a nice wxPython based IPython GUI. As you would expect, this GUI requires wxPython. Most Linux distributions have wxPython packages available and the built-in Python on OS X comes with wxPython preinstalled. For Windows, a binary installer is available on the `wxPython website <http://www.wxpython.org/>`_. No newline at end of file
212 Starting with IPython 0.9, IPython has a new IPython.frontend package that has a nice wxPython based IPython GUI. As you would expect, this GUI requires wxPython. Most Linux distributions have wxPython packages available and the built-in Python on OS X comes with wxPython preinstalled. For Windows, a binary installer is available on the `wxPython website <http://www.wxpython.org/>`_.
213
214 .. [Twisted] Twisted matrix. http://twistedmatrix.org
215 .. [ZopeInterface] http://pypi.python.org/pypi/zope.interface
216 .. [Foolscap] Foolscap network protocol. http://foolscap.lothar.com/trac
217 .. [pyOpenSSL] pyOpenSSL. http://pyopenssl.sourceforge.net No newline at end of file
@@ -3,7 +3,7 b' Using IPython for interactive work'
3 ==================================
3 ==================================
4
4
5 .. toctree::
5 .. toctree::
6 :maxdepth: 1
6 :maxdepth: 2
7
7
8 tutorial.txt
8 tutorial.txt
9 reference.txt
9 reference.txt
@@ -1,13 +1,7 b''
1 .. IPython documentation master file, created by sphinx-quickstart.py on Mon Mar 24 17:01:34 2008.
2 You can adapt this file completely to your liking, but it should at least
3 contain the root 'toctree' directive.
4
5 =================
1 =================
6 IPython reference
2 IPython reference
7 =================
3 =================
8
4
9 .. contents::
10
11 .. _command_line_options:
5 .. _command_line_options:
12
6
13 Command-line usage
7 Command-line usage
@@ -4,8 +4,6 b''
4 Quick IPython tutorial
4 Quick IPython tutorial
5 ======================
5 ======================
6
6
7 .. contents::
8
9 IPython can be used as an improved replacement for the Python prompt,
7 IPython can be used as an improved replacement for the Python prompt,
10 and for that you don't really need to read any more of this manual. But
8 and for that you don't really need to read any more of this manual. But
11 in this section we'll try to summarize a few tips on how to make the
9 in this section we'll try to summarize a few tips on how to make the
@@ -13,39 +13,43 b' IPython is licensed under the terms of the new or revised BSD license, as follow'
13
13
14 All rights reserved.
14 All rights reserved.
15
15
16 Redistribution and use in source and binary forms, with or without modification,
16 Redistribution and use in source and binary forms, with or without
17 are permitted provided that the following conditions are met:
17 modification, are permitted provided that the following conditions are
18
18 met:
19 Redistributions of source code must retain the above copyright notice, this list of
19
20 conditions and the following disclaimer.
20 Redistributions of source code must retain the above copyright notice,
21
21 this list of conditions and the following disclaimer.
22 Redistributions in binary form must reproduce the above copyright notice, this list
22
23 of conditions and the following disclaimer in the documentation and/or other
23 Redistributions in binary form must reproduce the above copyright notice,
24 materials provided with the distribution.
24 this list of conditions and the following disclaimer in the documentation
25
25 and/or other materials provided with the distribution.
26 Neither the name of the IPython Development Team nor the names of its contributors
26
27 may be used to endorse or promote products derived from this software without
27 Neither the name of the IPython Development Team nor the names of its
28 specific prior written permission.
28 contributors may be used to endorse or promote products derived from this
29
29 software without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
30
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
32 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
33 IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
34 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
38 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39 POSSIBILITY OF SUCH DAMAGE.
39 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
42
41 About the IPython Development Team
43 About the IPython Development Team
42 ==================================
44 ==================================
43
45
44 Fernando Perez began IPython in 2001 based on code from Janko Hauser <jhauser@zscout.de>
46 Fernando Perez began IPython in 2001 based on code from Janko Hauser
45 and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still the project lead.
47 <jhauser@zscout.de> and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still
48 the project lead.
46
49
47 The IPython Development Team is the set of all contributors to the IPython project.
50 The IPython Development Team is the set of all contributors to the IPython
48 This includes all of the IPython subprojects. Here is a list of the currently active contributors:
51 project. This includes all of the IPython subprojects. Here is a list of the
52 currently active contributors:
49
53
50 * Matthieu Brucher
54 * Matthieu Brucher
51 * Ondrej Certik
55 * Ondrej Certik
@@ -65,23 +69,23 b' If your name is missing, please add it.'
65 Our Copyright Policy
69 Our Copyright Policy
66 ====================
70 ====================
67
71
68 IPython uses a shared copyright model. Each contributor maintains copyright over
72 IPython uses a shared copyright model. Each contributor maintains copyright
69 their contributions to IPython. But, it is important to note that these
73 over their contributions to IPython. But, it is important to note that these
70 contributions are typically only changes to the repositories. Thus, the IPython
74 contributions are typically only changes to the repositories. Thus, the
71 source code, in its entirety is not the copyright of any single person or
75 IPython source code, in its entirety is not the copyright of any single person
72 institution. Instead, it is the collective copyright of the entire IPython
76 or institution. Instead, it is the collective copyright of the entire IPython
73 Development Team. If individual contributors want to maintain a record of what
77 Development Team. If individual contributors want to maintain a record of what
74 changes/contributions they have specific copyright on, they should indicate their
78 changes/contributions they have specific copyright on, they should indicate
75 copyright in the commit message of the change, when they commit the change to
79 their copyright in the commit message of the change, when they commit the
76 one of the IPython repositories.
80 change to one of the IPython repositories.
77
81
78 Miscellaneous
82 Miscellaneous
79 =============
83 =============
80
84
81 Some files (DPyGetOpt.py, for example) may be licensed under different
85 Some files (DPyGetOpt.py, for example) may be licensed under different
82 conditions. Ultimately each file indicates clearly the conditions under
86 conditions. Ultimately each file indicates clearly the conditions under which
83 which its author/authors have decided to publish the code.
87 its author/authors have decided to publish the code.
84
88
85 Versions of IPython up to and including 0.6.3 were released under the
89 Versions of IPython up to and including 0.6.3 were released under the GNU
86 GNU Lesser General Public License (LGPL), available at
90 Lesser General Public License (LGPL), available at
87 http://www.gnu.org/copyleft/lesser.html. No newline at end of file
91 http://www.gnu.org/copyleft/lesser.html.
@@ -8,7 +8,9 b' Using IPython for parallel computing'
8 :maxdepth: 2
8 :maxdepth: 2
9
9
10 parallel_intro.txt
10 parallel_intro.txt
11 parallel_process.txt
11 parallel_multiengine.txt
12 parallel_multiengine.txt
12 parallel_task.txt
13 parallel_task.txt
13 parallel_mpi.txt
14 parallel_mpi.txt
15 parallel_security.txt
14
16
@@ -4,20 +4,17 b''
4 Overview and getting started
4 Overview and getting started
5 ============================
5 ============================
6
6
7 .. contents::
8
9 Introduction
7 Introduction
10 ============
8 ============
11
9
12 This file gives an overview of IPython's sophisticated and
10 This section gives an overview of IPython's sophisticated and powerful
13 powerful architecture for parallel and distributed computing. This
11 architecture for parallel and distributed computing. This architecture
14 architecture abstracts out parallelism in a very general way, which
12 abstracts out parallelism in a very general way, which enables IPython to
15 enables IPython to support many different styles of parallelism
13 support many different styles of parallelism including:
16 including:
17
14
18 * Single program, multiple data (SPMD) parallelism.
15 * Single program, multiple data (SPMD) parallelism.
19 * Multiple program, multiple data (MPMD) parallelism.
16 * Multiple program, multiple data (MPMD) parallelism.
20 * Message passing using ``MPI``.
17 * Message passing using MPI.
21 * Task farming.
18 * Task farming.
22 * Data parallel.
19 * Data parallel.
23 * Combinations of these approaches.
20 * Combinations of these approaches.
@@ -97,8 +94,8 b' The controller also provides a single point of contact for users who wish'
97 to utilize the engines connected to the controller. There are different
94 to utilize the engines connected to the controller. There are different
98 ways of working with a controller. In IPython these ways correspond to different interfaces that the controller is adapted to. Currently we have two default interfaces to the controller:
95 ways of working with a controller. In IPython these ways correspond to different interfaces that the controller is adapted to. Currently we have two default interfaces to the controller:
99
96
100 * The MultiEngine interface, which provides the simplest possible way of working
97 * The MultiEngine interface, which provides the simplest possible way of
101 with engines interactively.
98 working with engines interactively.
102 * The Task interface, which provides presents the engines as a load balanced
99 * The Task interface, which provides presents the engines as a load balanced
103 task farming system.
100 task farming system.
104
101
@@ -124,169 +121,46 b' interface. Here are the two default clients:'
124 Security
121 Security
125 --------
122 --------
126
123
127 By default (as long as `pyOpenSSL` is installed) all network connections between the controller and engines and the controller and clients are secure. What does this mean? First of all, all of the connections will be encrypted using SSL. Second, the connections are authenticated. We handle authentication in a `capabilities`__ based security model. In this model, a "capability (known in some systems as a key) is a communicable, unforgeable token of authority". Put simply, a capability is like a key to your house. If you have the key to your house, you can get in. If not, you can't.
124 By default (as long as `pyOpenSSL` is installed) all network connections between the controller and engines and the controller and clients are secure. What does this mean? First of all, all of the connections will be encrypted using SSL. Second, the connections are authenticated. We handle authentication in a capability based security model [Capability]_. In this model, a "capability (known in some systems as a key) is a communicable, unforgeable token of authority". Put simply, a capability is like a key to your house. If you have the key to your house, you can get in. If not, you can't.
128
129 .. __: http://en.wikipedia.org/wiki/Capability-based_security
130
125
131 In our architecture, the controller is the only process that listens on network ports, and is thus responsible to creating these keys. In IPython, these keys are known as Foolscap URLs, or FURLs, because of the underlying network protocol we are using. As a user, you don't need to know anything about the details of these FURLs, other than that when the controller starts, it saves a set of FURLs to files named :file:`something.furl`. The default location of these files is the :file:`~./ipython/security` directory.
126 In our architecture, the controller is the only process that listens on network ports, and is thus responsible to creating these keys. In IPython, these keys are known as Foolscap URLs, or FURLs, because of the underlying network protocol we are using. As a user, you don't need to know anything about the details of these FURLs, other than that when the controller starts, it saves a set of FURLs to files named :file:`something.furl`. The default location of these files is the :file:`~./ipython/security` directory.
132
127
133 To connect and authenticate to the controller an engine or client simply needs to present an appropriate furl (that was originally created by the controller) to the controller. Thus, the .furl files need to be copied to a location where the clients and engines can find them. Typically, this is the :file:`~./ipython/security` directory on the host where the client/engine is running (which could be a different host than the controller). Once the .furl files are copied over, everything should work fine.
128 To connect and authenticate to the controller an engine or client simply needs to present an appropriate FURL (that was originally created by the controller) to the controller. Thus, the FURL files need to be copied to a location where the clients and engines can find them. Typically, this is the :file:`~./ipython/security` directory on the host where the client/engine is running (which could be a different host than the controller). Once the FURL files are copied over, everything should work fine.
134
129
135 Currently, there are three .furl files that the controller creates:
130 Currently, there are three FURL files that the controller creates:
136
131
137 ipcontroller-engine.furl
132 ipcontroller-engine.furl
138 This ``.furl`` file is the key that gives an engine the ability to connect
133 This FURL file is the key that gives an engine the ability to connect
139 to a controller.
134 to a controller.
140
135
141 ipcontroller-tc.furl
136 ipcontroller-tc.furl
142 This ``.furl`` file is the key that a :class:`TaskClient` must use to
137 This FURL file is the key that a :class:`TaskClient` must use to
143 connect to the task interface of a controller.
138 connect to the task interface of a controller.
144
139
145 ipcontroller-mec.furl
140 ipcontroller-mec.furl
146 This ``.furl`` file is the key that a :class:`MultiEngineClient` must use to
141 This FURL file is the key that a :class:`MultiEngineClient` must use
147 connect to the multiengine interface of a controller.
142 to connect to the multiengine interface of a controller.
148
143
149 More details of how these ``.furl`` files are used are given below.
144 More details of how these FURL files are used are given below.
145
146 A detailed description of the security model and its implementation in IPython
147 can be found :ref:`here <parallelsecurity>`.
150
148
151 Getting Started
149 Getting Started
152 ===============
150 ===============
153
151
154 To use IPython for parallel computing, you need to start one instance of
152 To use IPython for parallel computing, you need to start one instance of
155 the controller and one or more instances of the engine. The controller
153 the controller and one or more instances of the engine. Initially, it is best to simply start a controller and engines on a single host using the :command:`ipcluster` command. To start a controller and 4 engines on you localhost, just do::
156 and each engine can run on different machines or on the same machine.
157 Because of this, there are many different possibilities for setting up
158 the IP addresses and ports used by the various processes.
159
160 Starting the controller and engine on your local machine
161 --------------------------------------------------------
162
163 This is the simplest configuration that can be used and is useful for
164 testing the system and on machines that have multiple cores and/or
165 multple CPUs. The easiest way of getting started is to use the :command:`ipcluster`
166 command::
167
168 $ ipcluster -n 4
169
170 This will start an IPython controller and then 4 engines that connect to
171 the controller. Lastly, the script will print out the Python commands
172 that you can use to connect to the controller. It is that easy.
173
174 .. warning::
175
176 The :command:`ipcluster` does not currently work on Windows. We are
177 working on it though.
178
179 Underneath the hood, the controller creates ``.furl`` files in the
180 :file:`~./ipython/security` directory. Because the engines are on the
181 same host, they automatically find the needed :file:`ipcontroller-engine.furl`
182 there and use it to connect to the controller.
183
184 The :command:`ipcluster` script uses two other top-level
185 scripts that you can also use yourself. These scripts are
186 :command:`ipcontroller`, which starts the controller and :command:`ipengine` which
187 starts one engine. To use these scripts to start things on your local
188 machine, do the following.
189
190 First start the controller::
191
192 $ ipcontroller
193
194 Next, start however many instances of the engine you want using (repeatedly) the command::
195
196 $ ipengine
197
198 The engines should start and automatically connect to the controller using the ``.furl`` files in :file:`~./ipython/security`. You are now ready to use the controller and engines from IPython.
199
200 .. warning::
201
202 The order of the above operations is very important. You *must*
203 start the controller before the engines, since the engines connect
204 to the controller as they get started.
205
206 .. note::
207
208 On some platforms (OS X), to put the controller and engine into the background
209 you may need to give these commands in the form ``(ipcontroller &)``
210 and ``(ipengine &)`` (with the parentheses) for them to work properly.
211
212
213 Starting the controller and engines on different hosts
214 ------------------------------------------------------
215
216 When the controller and engines are running on different hosts, things are
217 slightly more complicated, but the underlying ideas are the same:
218
219 1. Start the controller on a host using :command:`ipcontroler`.
220 2. Copy :file:`ipcontroller-engine.furl` from :file:`~./ipython/security` on the controller's host to the host where the engines will run.
221 3. Use :command:`ipengine` on the engine's hosts to start the engines.
222
154
223 The only thing you have to be careful of is to tell :command:`ipengine` where the :file:`ipcontroller-engine.furl` file is located. There are two ways you can do this:
155 $ ipcluster local -n 4
224
156
225 * Put :file:`ipcontroller-engine.furl` in the :file:`~./ipython/security` directory
157 More details about starting the IPython controller and engines can be found :ref:`here <parallel_process>`
226 on the engine's host, where it will be found automatically.
227 * Call :command:`ipengine` with the ``--furl-file=full_path_to_the_file`` flag.
228
229 The ``--furl-file`` flag works like this::
230
231 $ ipengine --furl-file=/path/to/my/ipcontroller-engine.furl
232
233 .. note::
234
235 If the controller's and engine's hosts all have a shared file system
236 (:file:`~./ipython/security` is the same on all of them), then things
237 will just work!
238
239 Make .furl files persistent
240 ---------------------------
241
242 At fist glance it may seem that that managing the ``.furl`` files is a bit annoying. Going back to the house and key analogy, copying the ``.furl`` around each time you start the controller is like having to make a new key everytime you want to unlock the door and enter your house. As with your house, you want to be able to create the key (or ``.furl`` file) once, and then simply use it at any point in the future.
243
244 This is possible. The only thing you have to do is decide what ports the controller will listen on for the engines and clients. This is done as follows::
245
246 $ ipcontroller --client-port=10101 --engine-port=10102
247
248 Then, just copy the furl files over the first time and you are set. You can start and stop the controller and engines any many times as you want in the future, just make sure to tell the controller to use the *same* ports.
249
250 .. note::
251
252 You may ask the question: what ports does the controller listen on if you
253 don't tell is to use specific ones? The default is to use high random port
254 numbers. We do this for two reasons: i) to increase security through obcurity
255 and ii) to multiple controllers on a given host to start and automatically
256 use different ports.
257
258 Starting engines using ``mpirun``
259 ---------------------------------
260
261 The IPython engines can be started using ``mpirun``/``mpiexec``, even if
262 the engines don't call ``MPI_Init()`` or use the MPI API in any way. This is
263 supported on modern MPI implementations like `Open MPI`_.. This provides
264 an really nice way of starting a bunch of engine. On a system with MPI
265 installed you can do::
266
267 mpirun -n 4 ipengine
268
269 to start 4 engine on a cluster. This works even if you don't have any
270 Python-MPI bindings installed.
271
272 .. _Open MPI: http://www.open-mpi.org/
273
274 More details on using MPI with IPython can be found :ref:`here <parallelmpi>`.
275
276 Log files
277 ---------
278
279 All of the components of IPython have log files associated with them.
280 These log files can be extremely useful in debugging problems with
281 IPython and can be found in the directory ``~/.ipython/log``. Sending
282 the log files to us will often help us to debug any problems.
283
284 Next Steps
285 ==========
286
158
287 Once you have started the IPython controller and one or more engines, you
159 Once you have started the IPython controller and one or more engines, you
288 are ready to use the engines to do something useful. To make sure
160 are ready to use the engines to do something useful. To make sure
289 everything is working correctly, try the following commands::
161 everything is working correctly, try the following commands:
162
163 .. sourcecode:: ipython
290
164
291 In [1]: from IPython.kernel import client
165 In [1]: from IPython.kernel import client
292
166
@@ -310,7 +184,7 b' everything is working correctly, try the following commands::'
310 [3] In [1]: print "Hello World"
184 [3] In [1]: print "Hello World"
311 [3] Out[1]: Hello World
185 [3] Out[1]: Hello World
312
186
313 Remember, a client also needs to present a ``.furl`` file to the controller. How does this happen? When a multiengine client is created with no arguments, the client tries to find the corresponding ``.furl`` file in the local :file:`~./ipython/security` directory. If it finds it, you are set. If you have put the ``.furl`` file in a different location or it has a different name, create the client like this::
187 Remember, a client also needs to present a FURL file to the controller. How does this happen? When a multiengine client is created with no arguments, the client tries to find the corresponding FURL file in the local :file:`~./ipython/security` directory. If it finds it, you are set. If you have put the FURL file in a different location or it has a different name, create the client like this::
314
188
315 mec = client.MultiEngineClient('/path/to/my/ipcontroller-mec.furl')
189 mec = client.MultiEngineClient('/path/to/my/ipcontroller-mec.furl')
316
190
@@ -323,5 +197,9 b' You are now ready to learn more about the :ref:`MultiEngine <parallelmultiengine'
323 .. note::
197 .. note::
324
198
325 Don't forget that the engine, multiengine client and task client all have
199 Don't forget that the engine, multiengine client and task client all have
326 *different* furl files. You must move *each* of these around to an appropriate
200 *different* furl files. You must move *each* of these around to an
327 location so that the engines and clients can use them to connect to the controller.
201 appropriate location so that the engines and clients can use them to
202 connect to the controller.
203
204 .. [Capability] Capability-based security, http://en.wikipedia.org/wiki/Capability-based_security
205
@@ -4,19 +4,154 b''
4 Using MPI with IPython
4 Using MPI with IPython
5 =======================
5 =======================
6
6
7 The simplest way of getting started with MPI is to install an MPI implementation
7 Often, a parallel algorithm will require moving data between the engines. One way of accomplishing this is by doing a pull and then a push using the multiengine client. However, this will be slow as all the data has to go through the controller to the client and then back through the controller, to its final destination.
8 (we recommend `Open MPI`_) and `mpi4py`_ and then start the engines using the
8
9 ``mpirun`` command::
9 A much better way of moving data between engines is to use a message passing library, such as the Message Passing Interface (MPI) [MPI]_. IPython's parallel computing architecture has been designed from the ground up to integrate with MPI. This document describes how to use MPI with IPython.
10
10
11 mpirun -n 4 ipengine --mpi=mpi4py
11 Additional installation requirements
12
12 ====================================
13 This will automatically import `mpi4py`_ and make sure that `MPI_Init` is called
13
14 at the right time. We also have built in support for `PyTrilinos`_, which can be
14 If you want to use MPI with IPython, you will need to install:
15 used (assuming `PyTrilinos`_ is installed) by starting the engines with::
15
16
16 * A standard MPI implementation such as OpenMPI [OpenMPI]_ or MPICH.
17 mpirun -n 4 ipengine --mpi=pytrilinos
17 * The mpi4py [mpi4py]_ package.
18
18
19 .. _MPI: http://www-unix.mcs.anl.gov/mpi/
19 .. note::
20 .. _mpi4py: http://mpi4py.scipy.org/
20
21 .. _Open MPI: http://www.open-mpi.org/
21 The mpi4py package is not a strict requirement. However, you need to
22 .. _PyTrilinos: http://trilinos.sandia.gov/packages/pytrilinos/ No newline at end of file
22 have *some* way of calling MPI from Python. You also need some way of
23 making sure that :func:`MPI_Init` is called when the IPython engines start
24 up. There are a number of ways of doing this and a good number of
25 associated subtleties. We highly recommend just using mpi4py as it
26 takes care of most of these problems. If you want to do something
27 different, let us know and we can help you get started.
28
29 Starting the engines with MPI enabled
30 =====================================
31
32 To use code that calls MPI, there are typically two things that MPI requires.
33
34 1. The process that wants to call MPI must be started using
35 :command:`mpirun` or a batch system (like PBS) that has MPI support.
36 2. Once the process starts, it must call :func:`MPI_Init`.
37
38 There are a couple of ways that you can start the IPython engines and get these things to happen.
39
40 Automatic starting using :command:`mpirun` and :command:`ipcluster`
41 -------------------------------------------------------------------
42
43 The easiest approach is to use the `mpirun` mode of :command:`ipcluster`, which will first start a controller and then a set of engines using :command:`mpirun`::
44
45 $ ipcluster mpirun -n 4
46
47 This approach is best as interrupting :command:`ipcluster` will automatically
48 stop and clean up the controller and engines.
49
50 Manual starting using :command:`mpirun`
51 ---------------------------------------
52
53 If you want to start the IPython engines using the :command:`mpirun`, just do::
54
55 $ mpirun -n 4 ipengine --mpi=mpi4py
56
57 This requires that you already have a controller running and that the FURL
58 files for the engines are in place. We also have built in support for
59 PyTrilinos [PyTrilinos]_, which can be used (assuming is installed) by
60 starting the engines with::
61
62 mpirun -n 4 ipengine --mpi=pytrilinos
63
64 Automatic starting using PBS and :command:`ipcluster`
65 -----------------------------------------------------
66
67 The :command:`ipcluster` command also has built-in integration with PBS. For more information on this approach, see our documentation on :ref:`ipcluster <parallel_process>`.
68
69 Actually using MPI
70 ==================
71
72 Once the engines are running with MPI enabled, you are ready to go. You can now call any code that uses MPI in the IPython engines. And, all of this can be done interactively. Here we show a simple example that uses mpi4py [mpi4py]_.
73
74 First, lets define a simply function that uses MPI to calculate the sum of a distributed array. Save the following text in a file called :file:`psum.py`:
75
76 .. sourcecode:: python
77
78 from mpi4py import MPI
79 import numpy as np
80
81 def psum(a):
82 s = np.sum(a)
83 return MPI.COMM_WORLD.Allreduce(s,MPI.SUM)
84
85 Now, start an IPython cluster in the same directory as :file:`psum.py`::
86
87 $ ipcluster mpirun -n 4
88
89 Finally, connect to the cluster and use this function interactively. In this case, we create a random array on each engine and sum up all the random arrays using our :func:`psum` function:
90
91 .. sourcecode:: ipython
92
93 In [1]: from IPython.kernel import client
94
95 In [2]: mec = client.MultiEngineClient()
96
97 In [3]: mec.activate()
98
99 In [4]: px import numpy as np
100 Parallel execution on engines: all
101 Out[4]:
102 <Results List>
103 [0] In [13]: import numpy as np
104 [1] In [13]: import numpy as np
105 [2] In [13]: import numpy as np
106 [3] In [13]: import numpy as np
107
108 In [6]: px a = np.random.rand(100)
109 Parallel execution on engines: all
110 Out[6]:
111 <Results List>
112 [0] In [15]: a = np.random.rand(100)
113 [1] In [15]: a = np.random.rand(100)
114 [2] In [15]: a = np.random.rand(100)
115 [3] In [15]: a = np.random.rand(100)
116
117 In [7]: px from psum import psum
118 Parallel execution on engines: all
119 Out[7]:
120 <Results List>
121 [0] In [16]: from psum import psum
122 [1] In [16]: from psum import psum
123 [2] In [16]: from psum import psum
124 [3] In [16]: from psum import psum
125
126 In [8]: px s = psum(a)
127 Parallel execution on engines: all
128 Out[8]:
129 <Results List>
130 [0] In [17]: s = psum(a)
131 [1] In [17]: s = psum(a)
132 [2] In [17]: s = psum(a)
133 [3] In [17]: s = psum(a)
134
135 In [9]: px print s
136 Parallel execution on engines: all
137 Out[9]:
138 <Results List>
139 [0] In [18]: print s
140 [0] Out[18]: 187.451545803
141
142 [1] In [18]: print s
143 [1] Out[18]: 187.451545803
144
145 [2] In [18]: print s
146 [2] Out[18]: 187.451545803
147
148 [3] In [18]: print s
149 [3] Out[18]: 187.451545803
150
151 Any Python code that makes calls to MPI can be used in this manner, including
152 compiled C, C++ and Fortran libraries that have been exposed to Python.
153
154 .. [MPI] Message Passing Interface. http://www-unix.mcs.anl.gov/mpi/
155 .. [mpi4py] MPI for Python. mpi4py: http://mpi4py.scipy.org/
156 .. [OpenMPI] Open MPI. http://www.open-mpi.org/
157 .. [PyTrilinos] PyTrilinos. http://trilinos.sandia.gov/packages/pytrilinos/ No newline at end of file
@@ -4,8 +4,6 b''
4 IPython's multiengine interface
4 IPython's multiengine interface
5 ===============================
5 ===============================
6
6
7 .. contents::
8
9 The multiengine interface represents one possible way of working with a set of
7 The multiengine interface represents one possible way of working with a set of
10 IPython engines. The basic idea behind the multiengine interface is that the
8 IPython engines. The basic idea behind the multiengine interface is that the
11 capabilities of each engine are directly and explicitly exposed to the user.
9 capabilities of each engine are directly and explicitly exposed to the user.
@@ -21,7 +19,7 b' To follow along with this tutorial, you will need to start the IPython'
21 controller and four IPython engines. The simplest way of doing this is to use
19 controller and four IPython engines. The simplest way of doing this is to use
22 the :command:`ipcluster` command::
20 the :command:`ipcluster` command::
23
21
24 $ ipcluster -n 4
22 $ ipcluster local -n 4
25
23
26 For more detailed information about starting the controller and engines, see
24 For more detailed information about starting the controller and engines, see
27 our :ref:`introduction <ip1par>` to using IPython for parallel computing.
25 our :ref:`introduction <ip1par>` to using IPython for parallel computing.
@@ -30,7 +28,9 b' Creating a ``MultiEngineClient`` instance'
30 =========================================
28 =========================================
31
29
32 The first step is to import the IPython :mod:`IPython.kernel.client` module
30 The first step is to import the IPython :mod:`IPython.kernel.client` module
33 and then create a :class:`MultiEngineClient` instance::
31 and then create a :class:`MultiEngineClient` instance:
32
33 .. sourcecode:: ipython
34
34
35 In [1]: from IPython.kernel import client
35 In [1]: from IPython.kernel import client
36
36
@@ -38,13 +38,17 b' and then create a :class:`MultiEngineClient` instance::'
38
38
39 This form assumes that the :file:`ipcontroller-mec.furl` is in the
39 This form assumes that the :file:`ipcontroller-mec.furl` is in the
40 :file:`~./ipython/security` directory on the client's host. If not, the
40 :file:`~./ipython/security` directory on the client's host. If not, the
41 location of the ``.furl`` file must be given as an argument to the
41 location of the FURL file must be given as an argument to the
42 constructor::
42 constructor:
43
44 .. sourcecode:: ipython
43
45
44 In[2]: mec = client.MultiEngineClient('/path/to/my/ipcontroller-mec.furl')
46 In [2]: mec = client.MultiEngineClient('/path/to/my/ipcontroller-mec.furl')
45
47
46 To make sure there are engines connected to the controller, use can get a list
48 To make sure there are engines connected to the controller, use can get a list
47 of engine ids::
49 of engine ids:
50
51 .. sourcecode:: ipython
48
52
49 In [3]: mec.get_ids()
53 In [3]: mec.get_ids()
50 Out[3]: [0, 1, 2, 3]
54 Out[3]: [0, 1, 2, 3]
@@ -62,7 +66,9 b' Parallel map'
62 Python's builtin :func:`map` functions allows a function to be applied to a
66 Python's builtin :func:`map` functions allows a function to be applied to a
63 sequence element-by-element. This type of code is typically trivial to
67 sequence element-by-element. This type of code is typically trivial to
64 parallelize. In fact, the multiengine interface in IPython already has a
68 parallelize. In fact, the multiengine interface in IPython already has a
65 parallel version of :meth:`map` that works just like its serial counterpart::
69 parallel version of :meth:`map` that works just like its serial counterpart:
70
71 .. sourcecode:: ipython
66
72
67 In [63]: serial_result = map(lambda x:x**10, range(32))
73 In [63]: serial_result = map(lambda x:x**10, range(32))
68
74
@@ -84,7 +90,9 b' parallel version of :meth:`map` that works just like its serial counterpart::'
84 Parallel function decorator
90 Parallel function decorator
85 ---------------------------
91 ---------------------------
86
92
87 Parallel functions are just like normal function, but they can be called on sequences and *in parallel*. The multiengine interface provides a decorator that turns any Python function into a parallel function::
93 Parallel functions are just like normal function, but they can be called on sequences and *in parallel*. The multiengine interface provides a decorator that turns any Python function into a parallel function:
94
95 .. sourcecode:: ipython
88
96
89 In [10]: @mec.parallel()
97 In [10]: @mec.parallel()
90 ....: def f(x):
98 ....: def f(x):
@@ -110,7 +118,9 b' Blocking execution'
110 In blocking mode, the :class:`MultiEngineClient` object (called ``mec`` in
118 In blocking mode, the :class:`MultiEngineClient` object (called ``mec`` in
111 these examples) submits the command to the controller, which places the
119 these examples) submits the command to the controller, which places the
112 command in the engines' queues for execution. The :meth:`execute` call then
120 command in the engines' queues for execution. The :meth:`execute` call then
113 blocks until the engines are done executing the command::
121 blocks until the engines are done executing the command:
122
123 .. sourcecode:: ipython
114
124
115 # The default is to run on all engines
125 # The default is to run on all engines
116 In [4]: mec.execute('a=5')
126 In [4]: mec.execute('a=5')
@@ -130,7 +140,9 b' blocks until the engines are done executing the command::'
130 [3] In [2]: b=10
140 [3] In [2]: b=10
131
141
132 Python commands can be executed on specific engines by calling execute using
142 Python commands can be executed on specific engines by calling execute using
133 the ``targets`` keyword argument::
143 the ``targets`` keyword argument:
144
145 .. sourcecode:: ipython
134
146
135 In [6]: mec.execute('c=a+b',targets=[0,2])
147 In [6]: mec.execute('c=a+b',targets=[0,2])
136 Out[6]:
148 Out[6]:
@@ -163,7 +175,9 b' the ``targets`` keyword argument::'
163
175
164 This example also shows one of the most important things about the IPython
176 This example also shows one of the most important things about the IPython
165 engines: they have a persistent user namespaces. The :meth:`execute` method
177 engines: they have a persistent user namespaces. The :meth:`execute` method
166 returns a Python ``dict`` that contains useful information::
178 returns a Python ``dict`` that contains useful information:
179
180 .. sourcecode:: ipython
167
181
168 In [9]: result_dict = mec.execute('d=10; print d')
182 In [9]: result_dict = mec.execute('d=10; print d')
169
183
@@ -184,7 +198,9 b' then returns a :class:`PendingResult` object immediately. The'
184 :class:`PendingResult` object gives you a way of getting a result at a later
198 :class:`PendingResult` object gives you a way of getting a result at a later
185 time through its :meth:`get_result` method or :attr:`r` attribute. This allows
199 time through its :meth:`get_result` method or :attr:`r` attribute. This allows
186 you to quickly submit long running commands without blocking your local
200 you to quickly submit long running commands without blocking your local
187 Python/IPython session::
201 Python/IPython session:
202
203 .. sourcecode:: ipython
188
204
189 # In blocking mode
205 # In blocking mode
190 In [6]: mec.execute('import time')
206 In [6]: mec.execute('import time')
@@ -225,7 +241,9 b' Python/IPython session::'
225 Often, it is desirable to wait until a set of :class:`PendingResult` objects
241 Often, it is desirable to wait until a set of :class:`PendingResult` objects
226 are done. For this, there is a the method :meth:`barrier`. This method takes a
242 are done. For this, there is a the method :meth:`barrier`. This method takes a
227 tuple of :class:`PendingResult` objects and blocks until all of the associated
243 tuple of :class:`PendingResult` objects and blocks until all of the associated
228 results are ready::
244 results are ready:
245
246 .. sourcecode:: ipython
229
247
230 In [72]: mec.block=False
248 In [72]: mec.block=False
231
249
@@ -259,7 +277,9 b' and :attr:`targets`:'
259 * If no keyword argument is provided, the instance attributes are used.
277 * If no keyword argument is provided, the instance attributes are used.
260 * Keyword argument, if provided override the instance attributes.
278 * Keyword argument, if provided override the instance attributes.
261
279
262 The following examples demonstrate how to use the instance attributes::
280 The following examples demonstrate how to use the instance attributes:
281
282 .. sourcecode:: ipython
263
283
264 In [16]: mec.targets = [0,2]
284 In [16]: mec.targets = [0,2]
265
285
@@ -305,7 +325,9 b' that make it more pleasant to execute Python commands on the engines'
305 interactively. These are simply shortcuts to :meth:`execute` and
325 interactively. These are simply shortcuts to :meth:`execute` and
306 :meth:`get_result`. The ``%px`` magic executes a single Python command on the
326 :meth:`get_result`. The ``%px`` magic executes a single Python command on the
307 engines specified by the :attr:`targets` attribute of the
327 engines specified by the :attr:`targets` attribute of the
308 :class:`MultiEngineClient` instance (by default this is ``'all'``)::
328 :class:`MultiEngineClient` instance (by default this is ``'all'``):
329
330 .. sourcecode:: ipython
309
331
310 # Make this MultiEngineClient active for parallel magic commands
332 # Make this MultiEngineClient active for parallel magic commands
311 In [23]: mec.activate()
333 In [23]: mec.activate()
@@ -352,7 +374,9 b' engines specified by the :attr:`targets` attribute of the'
352
374
353 The ``%result`` magic gets and prints the stdin/stdout/stderr of the last
375 The ``%result`` magic gets and prints the stdin/stdout/stderr of the last
354 command executed on each engine. It is simply a shortcut to the
376 command executed on each engine. It is simply a shortcut to the
355 :meth:`get_result` method::
377 :meth:`get_result` method:
378
379 .. sourcecode:: ipython
356
380
357 In [29]: %result
381 In [29]: %result
358 Out[29]:
382 Out[29]:
@@ -370,7 +394,9 b' command executed on each engine. It is simply a shortcut to the'
370 [3] Out[9]: [ 0.83664764 -0.25602658]
394 [3] Out[9]: [ 0.83664764 -0.25602658]
371
395
372 The ``%autopx`` magic switches to a mode where everything you type is executed
396 The ``%autopx`` magic switches to a mode where everything you type is executed
373 on the engines given by the :attr:`targets` attribute::
397 on the engines given by the :attr:`targets` attribute:
398
399 .. sourcecode:: ipython
374
400
375 In [30]: mec.block=False
401 In [30]: mec.block=False
376
402
@@ -423,7 +449,9 b' are called :meth:`push` (sending an object to the engines) and :meth:`pull`'
423 Basic push and pull
449 Basic push and pull
424 -------------------
450 -------------------
425
451
426 Here are some examples of how you use :meth:`push` and :meth:`pull`::
452 Here are some examples of how you use :meth:`push` and :meth:`pull`:
453
454 .. sourcecode:: ipython
427
455
428 In [38]: mec.push(dict(a=1.03234,b=3453))
456 In [38]: mec.push(dict(a=1.03234,b=3453))
429 Out[38]: [None, None, None, None]
457 Out[38]: [None, None, None, None]
@@ -460,7 +488,9 b' Here are some examples of how you use :meth:`push` and :meth:`pull`::'
460 [3] Out[13]: speed
488 [3] Out[13]: speed
461
489
462 In non-blocking mode :meth:`push` and :meth:`pull` also return
490 In non-blocking mode :meth:`push` and :meth:`pull` also return
463 :class:`PendingResult` objects::
491 :class:`PendingResult` objects:
492
493 .. sourcecode:: ipython
464
494
465 In [47]: mec.block=False
495 In [47]: mec.block=False
466
496
@@ -474,8 +504,9 b' Push and pull for functions'
474 ---------------------------
504 ---------------------------
475
505
476 Functions can also be pushed and pulled using :meth:`push_function` and
506 Functions can also be pushed and pulled using :meth:`push_function` and
477 :meth:`pull_function`::
507 :meth:`pull_function`:
478
508
509 .. sourcecode:: ipython
479
510
480 In [52]: mec.block=True
511 In [52]: mec.block=True
481
512
@@ -518,7 +549,9 b' Dictionary interface'
518 As a shorthand to :meth:`push` and :meth:`pull`, the
549 As a shorthand to :meth:`push` and :meth:`pull`, the
519 :class:`MultiEngineClient` class implements some of the Python dictionary
550 :class:`MultiEngineClient` class implements some of the Python dictionary
520 interface. This make the remote namespaces of the engines appear as a local
551 interface. This make the remote namespaces of the engines appear as a local
521 dictionary. Underneath, this uses :meth:`push` and :meth:`pull`::
552 dictionary. Underneath, this uses :meth:`push` and :meth:`pull`:
553
554 .. sourcecode:: ipython
522
555
523 In [50]: mec.block=True
556 In [50]: mec.block=True
524
557
@@ -536,7 +569,9 b' follow that terminology. However, it is important to remember that in'
536 IPython's :class:`MultiEngineClient` class, :meth:`scatter` is from the
569 IPython's :class:`MultiEngineClient` class, :meth:`scatter` is from the
537 interactive IPython session to the engines and :meth:`gather` is from the
570 interactive IPython session to the engines and :meth:`gather` is from the
538 engines back to the interactive IPython session. For scatter/gather operations
571 engines back to the interactive IPython session. For scatter/gather operations
539 between engines, MPI should be used::
572 between engines, MPI should be used:
573
574 .. sourcecode:: ipython
540
575
541 In [58]: mec.scatter('a',range(16))
576 In [58]: mec.scatter('a',range(16))
542 Out[58]: [None, None, None, None]
577 Out[58]: [None, None, None, None]
@@ -569,7 +604,9 b' How to do parallel list comprehensions'
569
604
570 In many cases list comprehensions are nicer than using the map function. While
605 In many cases list comprehensions are nicer than using the map function. While
571 we don't have fully parallel list comprehensions, it is simple to get the
606 we don't have fully parallel list comprehensions, it is simple to get the
572 basic effect using :meth:`scatter` and :meth:`gather`::
607 basic effect using :meth:`scatter` and :meth:`gather`:
608
609 .. sourcecode:: ipython
573
610
574 In [66]: mec.scatter('x',range(64))
611 In [66]: mec.scatter('x',range(64))
575 Out[66]: [None, None, None, None]
612 Out[66]: [None, None, None, None]
@@ -598,7 +635,9 b' parallel command can actually raise multiple exceptions (one for each engine'
598 the command was run on). To express this idea, the MultiEngine interface has a
635 the command was run on). To express this idea, the MultiEngine interface has a
599 :exc:`CompositeError` exception class that will be raised in most cases. The
636 :exc:`CompositeError` exception class that will be raised in most cases. The
600 :exc:`CompositeError` class is a special type of exception that wraps one or
637 :exc:`CompositeError` class is a special type of exception that wraps one or
601 more other types of exceptions. Here is how it works::
638 more other types of exceptions. Here is how it works:
639
640 .. sourcecode:: ipython
602
641
603 In [76]: mec.block=True
642 In [76]: mec.block=True
604
643
@@ -628,7 +667,9 b' more other types of exceptions. Here is how it works::'
628 [2:execute]: ZeroDivisionError: integer division or modulo by zero
667 [2:execute]: ZeroDivisionError: integer division or modulo by zero
629 [3:execute]: ZeroDivisionError: integer division or modulo by zero
668 [3:execute]: ZeroDivisionError: integer division or modulo by zero
630
669
631 Notice how the error message printed when :exc:`CompositeError` is raised has information about the individual exceptions that were raised on each engine. If you want, you can even raise one of these original exceptions::
670 Notice how the error message printed when :exc:`CompositeError` is raised has information about the individual exceptions that were raised on each engine. If you want, you can even raise one of these original exceptions:
671
672 .. sourcecode:: ipython
632
673
633 In [80]: try:
674 In [80]: try:
634 ....: mec.execute('1/0')
675 ....: mec.execute('1/0')
@@ -652,7 +693,9 b' Notice how the error message printed when :exc:`CompositeError` is raised has in'
652
693
653 If you are working in IPython, you can simple type ``%debug`` after one of
694 If you are working in IPython, you can simple type ``%debug`` after one of
654 these :exc:`CompositeError` exceptions is raised, and inspect the exception
695 these :exc:`CompositeError` exceptions is raised, and inspect the exception
655 instance::
696 instance:
697
698 .. sourcecode:: ipython
656
699
657 In [81]: mec.execute('1/0')
700 In [81]: mec.execute('1/0')
658 ---------------------------------------------------------------------------
701 ---------------------------------------------------------------------------
@@ -734,7 +777,9 b' instance::'
734 The above example appears to be broken right now because of a change in
777 The above example appears to be broken right now because of a change in
735 how we are using Twisted.
778 how we are using Twisted.
736
779
737 All of this same error handling magic even works in non-blocking mode::
780 All of this same error handling magic even works in non-blocking mode:
781
782 .. sourcecode:: ipython
738
783
739 In [83]: mec.block=False
784 In [83]: mec.block=False
740
785
@@ -4,8 +4,6 b''
4 The IPython task interface
4 The IPython task interface
5 ==========================
5 ==========================
6
6
7 .. contents::
8
9 The task interface to the controller presents the engines as a fault tolerant, dynamic load-balanced system or workers. Unlike the multiengine interface, in the task interface, the user have no direct access to individual engines. In some ways, this interface is simpler, but in other ways it is more powerful.
7 The task interface to the controller presents the engines as a fault tolerant, dynamic load-balanced system or workers. Unlike the multiengine interface, in the task interface, the user have no direct access to individual engines. In some ways, this interface is simpler, but in other ways it is more powerful.
10
8
11 Best of all the user can use both of these interfaces running at the same time to take advantage or both of their strengths. When the user can break up the user's work into segments that do not depend on previous execution, the task interface is ideal. But it also has more power and flexibility, allowing the user to guide the distribution of jobs, without having to assign tasks to engines explicitly.
9 Best of all the user can use both of these interfaces running at the same time to take advantage or both of their strengths. When the user can break up the user's work into segments that do not depend on previous execution, the task interface is ideal. But it also has more power and flexibility, allowing the user to guide the distribution of jobs, without having to assign tasks to engines explicitly.
@@ -17,7 +15,7 b' To follow along with this tutorial, you will need to start the IPython'
17 controller and four IPython engines. The simplest way of doing this is to use
15 controller and four IPython engines. The simplest way of doing this is to use
18 the :command:`ipcluster` command::
16 the :command:`ipcluster` command::
19
17
20 $ ipcluster -n 4
18 $ ipcluster local -n 4
21
19
22 For more detailed information about starting the controller and engines, see
20 For more detailed information about starting the controller and engines, see
23 our :ref:`introduction <ip1par>` to using IPython for parallel computing.
21 our :ref:`introduction <ip1par>` to using IPython for parallel computing.
@@ -26,7 +24,9 b' Creating a ``TaskClient`` instance'
26 =========================================
24 =========================================
27
25
28 The first step is to import the IPython :mod:`IPython.kernel.client` module
26 The first step is to import the IPython :mod:`IPython.kernel.client` module
29 and then create a :class:`TaskClient` instance::
27 and then create a :class:`TaskClient` instance:
28
29 .. sourcecode:: ipython
30
30
31 In [1]: from IPython.kernel import client
31 In [1]: from IPython.kernel import client
32
32
@@ -34,10 +34,12 b' and then create a :class:`TaskClient` instance::'
34
34
35 This form assumes that the :file:`ipcontroller-tc.furl` is in the
35 This form assumes that the :file:`ipcontroller-tc.furl` is in the
36 :file:`~./ipython/security` directory on the client's host. If not, the
36 :file:`~./ipython/security` directory on the client's host. If not, the
37 location of the ``.furl`` file must be given as an argument to the
37 location of the FURL file must be given as an argument to the
38 constructor::
38 constructor:
39
40 .. sourcecode:: ipython
39
41
40 In[2]: mec = client.TaskClient('/path/to/my/ipcontroller-tc.furl')
42 In [2]: mec = client.TaskClient('/path/to/my/ipcontroller-tc.furl')
41
43
42 Quick and easy parallelism
44 Quick and easy parallelism
43 ==========================
45 ==========================
@@ -47,7 +49,9 b' In many cases, you simply want to apply a Python function to a sequence of objec'
47 Parallel map
49 Parallel map
48 ------------
50 ------------
49
51
50 The parallel :meth:`map` in the task interface is similar to that in the multiengine interface::
52 The parallel :meth:`map` in the task interface is similar to that in the multiengine interface:
53
54 .. sourcecode:: ipython
51
55
52 In [63]: serial_result = map(lambda x:x**10, range(32))
56 In [63]: serial_result = map(lambda x:x**10, range(32))
53
57
@@ -59,7 +63,9 b' The parallel :meth:`map` in the task interface is similar to that in the multien'
59 Parallel function decorator
63 Parallel function decorator
60 ---------------------------
64 ---------------------------
61
65
62 Parallel functions are just like normal function, but they can be called on sequences and *in parallel*. The multiengine interface provides a decorator that turns any Python function into a parallel function::
66 Parallel functions are just like normal function, but they can be called on sequences and *in parallel*. The multiengine interface provides a decorator that turns any Python function into a parallel function:
67
68 .. sourcecode:: ipython
63
69
64 In [10]: @tc.parallel()
70 In [10]: @tc.parallel()
65 ....: def f(x):
71 ....: def f(x):
General Comments 0
You need to be logged in to leave comments. Login now