##// END OF EJS Templates
main branch resync
laurent.dufrechou@gmail.com -
r1824:34b4cf01 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

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,155 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the frontendbase module."""
4
5 __docformat__ = "restructuredtext en"
6
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17
18 import unittest
19
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
28 from IPython.testing.decorators import skip
29
30 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 """FrontEndBase subclass for checking callbacks"""
32 def __init__(self, engine=None, history=None):
33 super(FrontEndCallbackChecker, self).__init__(engine=engine,
34 history=history)
35 self.updateCalled = False
36 self.renderResultCalled = False
37 self.renderErrorCalled = False
38
39 def update_cell_prompt(self, result, blockID=None):
40 self.updateCalled = True
41 return result
42
43 def render_result(self, result):
44 self.renderResultCalled = True
45 return result
46
47
48 def render_error(self, failure):
49 self.renderErrorCalled = True
50 return failure
51
52
53
54
55 class TestAsyncFrontendBase(unittest.TestCase):
56 def setUp(self):
57 """Setup the EngineService and FrontEndBase"""
58
59 self.fb = FrontEndCallbackChecker(engine=EngineService())
60
61 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
63 AsyncFrontEndBase))
64
65 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 block = """def test(a):"""
69
70 assert(self.fb.is_complete(block) == False)
71
72 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 block = """def test(a): pass"""
76
77 assert(self.fb.is_complete(block))
78
79 block = """a=3"""
80
81 assert(self.fb.is_complete(block))
82
83 def test_blockID_added_to_result(self):
84 block = """3+3"""
85
86 d = self.fb.execute(block, blockID='TEST_ID')
87
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
89
90 def test_blockID_added_to_failure(self):
91 block = "raise Exception()"
92
93 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
95
96 def checkBlockID(self, result, expected=""):
97 assert(result['blockID'] == expected)
98
99
100 def checkFailureID(self, failure, expected=""):
101 assert(failure.blockID == expected)
102
103
104 def test_callbacks_added_to_execute(self):
105 """test that
106 update_cell_prompt
107 render_result
108
109 are added to execute request
110 """
111
112 d = self.fb.execute("10+10")
113 d.addCallback(self.checkCallbacks)
114
115 def checkCallbacks(self, result):
116 assert(self.fb.updateCalled)
117 assert(self.fb.renderResultCalled)
118
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
120 def test_error_callback_added_to_execute(self):
121 """test that render_error called on execution error"""
122
123 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
125
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
128
129 def test_history_returns_expected_block(self):
130 """Make sure history browsing doesn't fail"""
131
132 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
134 d = self.fb.execute(b)
135
136 # d is now the deferred for the last executed block
137 d.addCallback(self.historyTests, blocks)
138
139
140 def historyTests(self, result, blocks):
141 """historyTests"""
142
143 assert(len(blocks) >= 3)
144 assert(self.fb.get_history_previous("") == blocks[-2])
145 assert(self.fb.get_history_previous("") == blocks[-3])
146 assert(self.fb.get_history_next() == blocks[-2])
147
148
149 def test_history_returns_none_at_startup(self):
150 """test_history_returns_none_at_startup"""
151
152 assert(self.fb.get_history_previous("")==None)
153 assert(self.fb.get_history_next()==None)
154
155
@@ -0,0 +1,26 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the interpreter.py module."""
4
5 __docformat__ = "restructuredtext en"
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 from IPython.kernel.core.interpreter import Interpreter
19
20 def test_unicode():
21 """ Test unicode handling with the interpreter.
22 """
23 i = Interpreter()
24 i.execute_python(u'print "ù"')
25 i.execute_python('print "ù"')
26
@@ -0,0 +1,53 b''
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """IPython Test Suite Runner.
4 """
5
6 import sys
7 import warnings
8
9 from nose.core import TestProgram
10 import nose.plugins.builtin
11
12 from IPython.testing.plugin.ipdoctest import IPythonDoctest
13
14 def main():
15 """Run the IPython test suite.
16 """
17
18 warnings.filterwarnings('ignore',
19 'This will be removed soon. Use IPython.testing.util instead')
20
21
22 # construct list of plugins, omitting the existing doctest plugin
23 plugins = [IPythonDoctest()]
24 for p in nose.plugins.builtin.plugins:
25 plug = p()
26 if plug.name == 'doctest':
27 continue
28
29 #print 'adding plugin:',plug.name # dbg
30 plugins.append(plug)
31
32 argv = sys.argv + ['--doctest-tests','--doctest-extension=txt',
33 '--detailed-errors',
34
35 # We add --exe because of setuptools' imbecility (it
36 # blindly does chmod +x on ALL files). Nose does the
37 # right thing and it tries to avoid executables,
38 # setuptools unfortunately forces our hand here. This
39 # has been discussed on the distutils list and the
40 # setuptools devs refuse to fix this problem!
41 '--exe',
42 ]
43
44 has_ip = False
45 for arg in sys.argv:
46 if 'IPython' in arg:
47 has_ip = True
48 break
49
50 if not has_ip:
51 argv.append('IPython')
52
53 TestProgram(argv=argv,plugins=plugins)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,161 b''
1 """Tests for the decorators we've created for IPython.
2 """
3
4 # Module imports
5 # Std lib
6 import inspect
7 import sys
8
9 # Third party
10 import nose.tools as nt
11
12 # Our own
13 from IPython.testing import decorators as dec
14
15
16 #-----------------------------------------------------------------------------
17 # Utilities
18
19 # Note: copied from OInspect, kept here so the testing stuff doesn't create
20 # circular dependencies and is easier to reuse.
21 def getargspec(obj):
22 """Get the names and default values of a function's arguments.
23
24 A tuple of four things is returned: (args, varargs, varkw, defaults).
25 'args' is a list of the argument names (it may contain nested lists).
26 'varargs' and 'varkw' are the names of the * and ** arguments or None.
27 'defaults' is an n-tuple of the default values of the last n arguments.
28
29 Modified version of inspect.getargspec from the Python Standard
30 Library."""
31
32 if inspect.isfunction(obj):
33 func_obj = obj
34 elif inspect.ismethod(obj):
35 func_obj = obj.im_func
36 else:
37 raise TypeError, 'arg is not a Python function'
38 args, varargs, varkw = inspect.getargs(func_obj.func_code)
39 return args, varargs, varkw, func_obj.func_defaults
40
41 #-----------------------------------------------------------------------------
42 # Testing functions
43
44 @dec.skip
45 def test_deliberately_broken():
46 """A deliberately broken test - we want to skip this one."""
47 1/0
48
49 @dec.skip('foo')
50 def test_deliberately_broken2():
51 """Another deliberately broken test - we want to skip this one."""
52 1/0
53
54
55 # Verify that we can correctly skip the doctest for a function at will, but
56 # that the docstring itself is NOT destroyed by the decorator.
57 @dec.skip_doctest
58 def doctest_bad(x,y=1,**k):
59 """A function whose doctest we need to skip.
60
61 >>> 1+1
62 3
63 """
64 print 'x:',x
65 print 'y:',y
66 print 'k:',k
67
68
69 def call_doctest_bad():
70 """Check that we can still call the decorated functions.
71
72 >>> doctest_bad(3,y=4)
73 x: 3
74 y: 4
75 k: {}
76 """
77 pass
78
79
80 def test_skip_dt_decorator():
81 """Doctest-skipping decorator should preserve the docstring.
82 """
83 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
84 check = """A function whose doctest we need to skip.
85
86 >>> 1+1
87 3
88 """
89 # Fetch the docstring from doctest_bad after decoration.
90 val = doctest_bad.__doc__
91
92 assert check==val,"doctest_bad docstrings don't match"
93
94 # Doctest skipping should work for class methods too
95 class foo(object):
96 """Foo
97
98 Example:
99
100 >>> 1+1
101 2
102 """
103
104 @dec.skip_doctest
105 def __init__(self,x):
106 """Make a foo.
107
108 Example:
109
110 >>> f = foo(3)
111 junk
112 """
113 print 'Making a foo.'
114 self.x = x
115
116 @dec.skip_doctest
117 def bar(self,y):
118 """Example:
119
120 >>> f = foo(3)
121 >>> f.bar(0)
122 boom!
123 >>> 1/0
124 bam!
125 """
126 return 1/y
127
128 def baz(self,y):
129 """Example:
130
131 >>> f = foo(3)
132 Making a foo.
133 >>> f.baz(3)
134 True
135 """
136 return self.x==y
137
138
139
140 def test_skip_dt_decorator2():
141 """Doctest-skipping decorator should preserve function signature.
142 """
143 # Hardcoded correct answer
144 dtargs = (['x', 'y'], None, 'k', (1,))
145 # Introspect out the value
146 dtargsr = getargspec(doctest_bad)
147 assert dtargsr==dtargs, \
148 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
149
150
151 @dec.skip_linux
152 def test_linux():
153 nt.assert_not_equals(sys.platform,'linux2',"This test can't run under linux")
154
155 @dec.skip_win32
156 def test_win32():
157 nt.assert_not_equals(sys.platform,'win32',"This test can't run under windows")
158
159 @dec.skip_osx
160 def test_osx():
161 nt.assert_not_equals(sys.platform,'darwin',"This test can't run under osx")
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,32 b''
1 # encoding: utf-8
2
3 """Tests for genutils.py"""
4
5 __docformat__ = "restructuredtext en"
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 from IPython import genutils
19
20
21 def test_get_home_dir():
22 """Make sure we can get the home directory."""
23 home_dir = genutils.get_home_dir()
24
25 def test_get_ipython_dir():
26 """Make sure we can get the ipython directory."""
27 ipdir = genutils.get_ipython_dir()
28
29 def test_get_security_dir():
30 """Make sure we can get the ipython/security directory."""
31 sdir = genutils.get_security_dir()
32 No newline at end of file
@@ -0,0 +1,21 b''
1 """ Tests for various magic functions
2
3 Needs to be run by nose (to make ipython session available)
4
5 """
6 def test_rehashx():
7 # clear up everything
8 _ip.IP.alias_table.clear()
9 del _ip.db['syscmdlist']
10
11 _ip.magic('rehashx')
12 # Practically ALL ipython development systems will have more than 10 aliases
13
14 assert len(_ip.IP.alias_table) > 10
15 for key, val in _ip.IP.alias_table.items():
16 # we must strip dots from alias names
17 assert '.' not in key
18
19 # rehashx must fill up syscmdlist
20 scoms = _ip.db['syscmdlist']
21 assert len(scoms) > 10
@@ -0,0 +1,22 b''
1 # If you want ipython to appear in a linux app launcher ("start menu"), install this by doing:
2 # sudo desktop-file-install ipython-sh.desktop
3
4 [Desktop Entry]
5 Comment=Perform shell-like tasks in interactive ipython session
6 Exec=ipython -p sh
7 GenericName[en_US]=IPython shell mode
8 GenericName=IPython shell mode
9 Icon=gnome-netstatus-idle
10 MimeType=
11 Name[en_US]=ipython-sh
12 Name=ipython-sh
13 Path=
14 Categories=Development;Utility;
15 StartupNotify=false
16 Terminal=true
17 TerminalOptions=
18 Type=Application
19 X-DBUS-ServiceName=
20 X-DBUS-StartupType=none
21 X-KDE-SubstituteUID=false
22 X-KDE-Username=
@@ -0,0 +1,22 b''
1 # If you want ipython to appear in a linux app launcher ("start menu"), install this by doing:
2 # sudo desktop-file-install ipython.desktop
3
4 [Desktop Entry]
5 Comment=Enhanced interactive Python shell
6 Exec=ipython
7 GenericName[en_US]=IPython
8 GenericName=IPython
9 Icon=gnome-netstatus-idle
10 MimeType=
11 Name[en_US]=ipython
12 Name=ipython
13 Path=
14 Categories=Development;Utility;
15 StartupNotify=false
16 Terminal=true
17 TerminalOptions=
18 Type=Application
19 X-DBUS-ServiceName=
20 X-DBUS-StartupType=none
21 X-KDE-SubstituteUID=false
22 X-KDE-Username=
@@ -0,0 +1,240 b''
1 .. _paralleltask:
2
3 ==========================
4 The IPython task interface
5 ==========================
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. Best of all the user can use both of these interfaces 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.
10
11 Starting the IPython controller and engines
12 ===========================================
13
14 To follow along with this tutorial, the user will need to start the IPython
15 controller and four IPython engines. The simplest way of doing this is to
16 use the ``ipcluster`` command::
17
18 $ ipcluster -n 4
19
20 For more detailed information about starting the controller and engines, see our :ref:`introduction <ip1par>` to using IPython for parallel computing.
21
22 The magic here is that this single controller and set of engines is running both the MultiEngine and ``Task`` interfaces simultaneously.
23
24 QuickStart Task Farming
25 =======================
26
27 First, a quick example of how to start running the most basic Tasks.
28 The first step is to import the IPython ``client`` module and then create a ``TaskClient`` instance::
29
30 In [1]: from IPython.kernel import client
31
32 In [2]: tc = client.TaskClient()
33
34 Then the user wrap the commands the user want to run in Tasks::
35
36 In [3]: tasklist = []
37 In [4]: for n in range(1000):
38 ... tasklist.append(client.Task("a = %i"%n, pull="a"))
39
40 The first argument of the ``Task`` constructor is a string, the command to be executed. The most important optional keyword argument is ``pull``, which can be a string or list of strings, and it specifies the variable names to be saved as results of the ``Task``.
41
42 Next, the user need to submit the Tasks to the ``TaskController`` with the ``TaskClient``::
43
44 In [5]: taskids = [ tc.run(t) for t in tasklist ]
45
46 This will give the user a list of the TaskIDs used by the controller to keep track of the Tasks and their results. Now at some point the user are going to want to get those results back. The ``barrier`` method allows the user to wait for the Tasks to finish running::
47
48 In [6]: tc.barrier(taskids)
49
50 This command will block until all the Tasks in ``taskids`` have finished. Now, the user probably want to look at the user's results::
51
52 In [7]: task_results = [ tc.get_task_result(taskid) for taskid in taskids ]
53
54 Now the user have a list of ``TaskResult`` objects, which have the actual result as a dictionary, but also keep track of some useful metadata about the ``Task``::
55
56 In [8]: tr = ``Task``_results[73]
57
58 In [9]: tr
59 Out[9]: ``TaskResult``[ID:73]:{'a':73}
60
61 In [10]: tr.engineid
62 Out[10]: 1
63
64 In [11]: tr.submitted, tr.completed, tr.duration
65 Out[11]: ("2008/03/08 03:41:42", "2008/03/08 03:41:44", 2.12345)
66
67 The actual results are stored in a dictionary, ``tr.results``, and a namespace object ``tr.ns`` which accesses the result keys by attribute::
68
69 In [12]: tr.results['a']
70 Out[12]: 73
71
72 In [13]: tr.ns.a
73 Out[13]: 73
74
75 That should cover the basics of running simple Tasks. There are several more powerful things the user can do with Tasks covered later. The most useful probably being using a ``MutiEngineClient`` interface to initialize all the engines with the import dependencies necessary to run the user's Tasks.
76
77 There are many options for running and managing Tasks. The best way to learn further about the ``Task`` interface is to study the examples in ``docs/examples``. If the user do so and learn a lots about this interface, we encourage the user to expand this documentation about the ``Task`` system.
78
79 Overview of the Task System
80 ===========================
81
82 The user's view of the ``Task`` system has three basic objects: The ``TaskClient``, the ``Task``, and the ``TaskResult``. The names of these three objects well indicate their role.
83
84 The ``TaskClient`` is the user's ``Task`` farming connection to the IPython cluster. Unlike the ``MultiEngineClient``, the ``TaskControler`` handles all the scheduling and distribution of work, so the ``TaskClient`` has no notion of engines, it just submits Tasks and requests their results. The Tasks are described as ``Task`` objects, and their results are wrapped in ``TaskResult`` objects. Thus, there are very few necessary methods for the user to manage.
85
86 Inside the task system is a Scheduler object, which assigns tasks to workers. The default scheduler is a simple FIFO queue. Subclassing the Scheduler should be easy, just implementing your own priority system.
87
88 The TaskClient
89 ==============
90
91 The ``TaskClient`` is the object the user use to connect to the ``Controller`` that is managing the user's Tasks. It is the analog of the ``MultiEngineClient`` for the standard IPython multiplexing interface. As with all client interfaces, the first step is to import the IPython Client Module::
92
93 In [1]: from IPython.kernel import client
94
95 Just as with the ``MultiEngineClient``, the user create the ``TaskClient`` with a tuple, containing the ip-address and port of the ``Controller``. the ``client`` module conveniently has the default address of the ``Task`` interface of the controller. Creating a default ``TaskClient`` object would be done with this::
96
97 In [2]: tc = client.TaskClient(client.default_task_address)
98
99 or, if the user want to specify a non default location of the ``Controller``, the user can specify explicitly::
100
101 In [3]: tc = client.TaskClient(("192.168.1.1", 10113))
102
103 As discussed earlier, the ``TaskClient`` only has a few basic methods.
104
105 * ``tc.run(task)``
106 ``run`` is the method by which the user submits Tasks. It takes exactly one argument, a ``Task`` object. All the advanced control of ``Task`` behavior is handled by properties of the ``Task`` object, rather than the submission command, so they will be discussed later in the `Task`_ section. ``run`` returns an integer, the ``Task``ID by which the ``Task`` and its results can be tracked and retrieved::
107
108 In [4]: ``Task``ID = tc.run(``Task``)
109
110 * ``tc.get_task_result(taskid, block=``False``)``
111 ``get_task_result`` is the method by which results are retrieved. It takes a single integer argument, the ``Task``ID`` of the result the user wish to retrieve. ``get_task_result`` also takes a keyword argument ``block``. ``block`` specifies whether the user actually want to wait for the result. If ``block`` is false, as it is by default, ``get_task_result`` will return immediately. If the ``Task`` has completed, it will return the ``TaskResult`` object for that ``Task``. But if the ``Task`` has not completed, it will return ``None``. If the user specify ``block=``True``, then ``get_task_result`` will wait for the ``Task`` to complete, and always return the ``TaskResult`` for the requested ``Task``.
112 * ``tc.barrier(taskid(s))``
113 ``barrier`` is a synchronization method. It takes exactly one argument, a ``Task``ID or list of taskIDs. ``barrier`` will block until all the specified Tasks have completed. In practice, a barrier is often called between the ``Task`` submission section of the code and the result gathering section::
114
115 In [5]: taskIDs = [ tc.run(``Task``) for ``Task`` in myTasks ]
116
117 In [6]: tc.get_task_result(taskIDs[-1]) is None
118 Out[6]: ``True``
119
120 In [7]: tc.barrier(``Task``ID)
121
122 In [8]: results = [ tc.get_task_result(tid) for tid in taskIDs ]
123
124 * ``tc.queue_status(verbose=``False``)``
125 ``queue_status`` is a method for querying the state of the ``TaskControler``. ``queue_status`` returns a dict of the form::
126
127 {'scheduled': Tasks that have been submitted but yet run
128 'pending' : Tasks that are currently running
129 'succeeded': Tasks that have completed successfully
130 'failed' : Tasks that have finished with a failure
131 }
132
133 if @verbose is not specified (or is ``False``), then the values of the dict are integers - the number of Tasks in each state. if @verbose is ``True``, then each element in the dict is a list of the taskIDs in that state::
134
135 In [8]: tc.queue_status()
136 Out[8]: {'scheduled': 4,
137 'pending' : 2,
138 'succeeded': 5,
139 'failed' : 1
140 }
141
142 In [9]: tc.queue_status(verbose=True)
143 Out[9]: {'scheduled': [8,9,10,11],
144 'pending' : [6,7],
145 'succeeded': [0,1,2,4,5],
146 'failed' : [3]
147 }
148
149 * ``tc.abort(taskid)``
150 ``abort`` allows the user to abort Tasks that have already been submitted. ``abort`` will always return immediately. If the ``Task`` has completed, ``abort`` will raise an ``IndexError ``Task`` Already Completed``. An obvious case for ``abort`` would be where the user submits a long-running ``Task`` with a number of retries (see ``Task``_ section for how to specify retries) in an interactive session, but realizes there has been a typo. The user can then abort the ``Task``, preventing certain failures from cluttering up the queue. It can also be used for parallel search-type problems, where only one ``Task`` will give the solution, so once the user find the solution, the user would want to abort all remaining Tasks to prevent wasted work.
151 * ``tc.spin()``
152 ``spin`` simply triggers the scheduler in the ``TaskControler``. Under most normal circumstances, this will do nothing. The primary known usage case involves the ``Task`` dependency (see `Dependencies`_). The dependency is a function of an Engine's ``properties``, but changing the ``properties`` via the ``MutliEngineClient`` does not trigger a reschedule event. The main example case for this requires the following event sequence:
153 * ``engine`` is available, ``Task`` is submitted, but ``engine`` does not have ``Task``'s dependencies.
154 * ``engine`` gets necessary dependencies while no new Tasks are submitted or completed.
155 * now ``engine`` can run ``Task``, but a ``Task`` event is required for the ``TaskControler`` to try scheduling ``Task`` again.
156
157 ``spin`` is just an empty ping method to ensure that the Controller has scheduled all available Tasks, and should not be needed under most normal circumstances.
158
159 That covers the ``TaskClient``, a simple interface to the cluster. With this, the user can submit jobs (and abort if necessary), request their results, synchronize on arbitrary subsets of jobs.
160
161 .. _task: The Task Object
162
163 The Task Object
164 ===============
165
166 The ``Task`` is the basic object for describing a job. It can be used in a very simple manner, where the user just specifies a command string to be executed as the ``Task``. The usage of this first argument is exactly the same as the ``execute`` method of the ``MultiEngine`` (in fact, ``execute`` is called to run the code)::
167
168 In [1]: t = client.Task("a = str(id)")
169
170 This ``Task`` would run, and store the string representation of the ``id`` element in ``a`` in each worker's namespace, but it is fairly useless because the user does not know anything about the state of the ``worker`` on which it ran at the time of retrieving results. It is important that each ``Task`` not expect the state of the ``worker`` to persist after the ``Task`` is completed.
171 There are many different situations for using ``Task`` Farming, and the ``Task`` object has many attributes for use in customizing the ``Task`` behavior. All of a ``Task``'s attributes may be specified in the constructor, through keyword arguments, or after ``Task`` construction through attribute assignment.
172
173 Data Attributes
174 ***************
175 It is likely that the user may want to move data around before or after executing the ``Task``. We provide methods of sending data to initialize the worker's namespace, and specifying what data to bring back as the ``Task``'s results.
176
177 * pull = []
178 The obvious case is as above, where ``t`` would execute and store the result of ``myfunc`` in ``a``, it is likely that the user would want to bring ``a`` back to their namespace. This is done through the ``pull`` attribute. ``pull`` can be a string or list of strings, and it specifies the names of variables to be retrieved. The ``TaskResult`` object retrieved by ``get_task_result`` will have a dictionary of keys and values, and the ``Task``'s ``pull`` attribute determines what goes into it::
179
180 In [2]: t = client.Task("a = str(id)", pull = "a")
181
182 In [3]: t = client.Task("a = str(id)", pull = ["a", "id"])
183
184 * push = {}
185 A user might also want to initialize some data into the namespace before the code part of the ``Task`` is run. Enter ``push``. ``push`` is a dictionary of key/value pairs to be loaded from the user's namespace into the worker's immediately before execution::
186
187 In [4]: t = client.Task("a = f(submitted)", push=dict(submitted=time.time()), pull="a")
188
189 push and pull result directly in calling an ``engine``'s ``push`` and ``pull`` methods before and after ``Task`` execution respectively, and thus their api is the same.
190
191 Namespace Cleaning
192 ******************
193 When a user is running a large number of Tasks, it is likely that the namespace of the worker's could become cluttered. Some Tasks might be sensitive to clutter, while others might be known to cause namespace pollution. For these reasons, Tasks have two boolean attributes for cleaning up the namespace.
194
195 * ``clear_after``
196 if clear_after is specified ``True``, the worker on which the ``Task`` was run will be reset (via ``engine.reset``) upon completion of the ``Task``. This can be useful for both Tasks that produce clutter or Tasks whose intermediate data one might wish to be kept private::
197
198 In [5]: t = client.Task("a = range(1e10)", pull = "a",clear_after=True)
199
200
201 * ``clear_before``
202 as one might guess, clear_before is identical to ``clear_after``, but it takes place before the ``Task`` is run. This ensures that the ``Task`` runs on a fresh worker::
203
204 In [6]: t = client.Task("a = globals()", pull = "a",clear_before=True)
205
206 Of course, a user can both at the same time, ensuring that all workers are clear except when they are currently running a job. Both of these default to ``False``.
207
208 Fault Tolerance
209 ***************
210 It is possible that Tasks might fail, and there are a variety of reasons this could happen. One might be that the worker it was running on disconnected, and there was nothing wrong with the ``Task`` itself. With the fault tolerance attributes of the ``Task``, the user can specify how many times to resubmit the ``Task``, and what to do if it never succeeds.
211
212 * ``retries``
213 ``retries`` is an integer, specifying the number of times a ``Task`` is to be retried. It defaults to zero. It is often a good idea for this number to be 1 or 2, to protect the ``Task`` from disconnecting engines, but not a large number. If a ``Task`` is failing 100 times, there is probably something wrong with the ``Task``. The canonical bad example:
214
215 In [7]: t = client.Task("os.kill(os.getpid(), 9)", retries=99)
216
217 This would actually take down 100 workers.
218
219 * ``recovery_task``
220 ``recovery_task`` is another ``Task`` object, to be run in the event of the original ``Task`` still failing after running out of retries. Since ``recovery_task`` is another ``Task`` object, it can have its own ``recovery_task``. The chain of Tasks is limitless, except loops are not allowed (that would be bad!).
221
222 Dependencies
223 ************
224 Dependencies are the most powerful part of the ``Task`` farming system, because it allows the user to do some classification of the workers, and guide the ``Task`` distribution without meddling with the controller directly. It makes use of two objects - the ``Task``'s ``depend`` attribute, and the engine's ``properties``. See the `MultiEngine`_ reference for how to use engine properties. The engine properties api exists for extending IPython, allowing conditional execution and new controllers that make decisions based on properties of its engines. Currently the ``Task`` dependency is the only internal use of the properties api.
225
226 .. _MultiEngine: ./parallel_multiengine
227
228 The ``depend`` attribute of a ``Task`` must be a function of exactly one argument, the worker's properties dictionary, and it should return ``True`` if the ``Task`` should be allowed to run on the worker and ``False`` if not. The usage in the controller is fault tolerant, so exceptions raised by ``Task.depend`` will be ignored and functionally equivalent to always returning ``False``. Tasks`` with invalid ``depend`` functions will never be assigned to a worker::
229
230 In [8]: def dep(properties):
231 ... return properties["RAM"] > 2**32 # have at least 4GB
232 In [9]: t = client.Task("a = bigfunc()", depend=dep)
233
234 It is important to note that assignment of values to the properties dict is done entirely by the user, either locally (in the engine) using the EngineAPI, or remotely, through the ``MultiEngineClient``'s get/set_properties methods.
235
236
237
238
239
240
@@ -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,217 b''
1 Overview
2 ========
3
4 This document describes the steps required to install IPython. IPython is organized into a number of subpackages, each of which has its own dependencies. All of the subpackages come with IPython, so you don't need to download and install them separately. However, to use a given subpackage, you will need to install all of its dependencies.
5
6
7 Please let us know if you have problems installing IPython or any of its
8 dependencies. IPython requires Python version 2.4 or greater. We have not tested
9 IPython with the upcoming 2.6 or 3.0 versions.
10
11 .. warning::
12
13 IPython will not work with Python 2.3 or below.
14
15 Some of the installation approaches use the :mod:`setuptools` package and its :command:`easy_install` command line program. In many scenarios, this provides the most simple method of installing IPython and its dependencies. It is not required though. More information about :mod:`setuptools` can be found on its website.
16
17 More general information about installing Python packages can be found in Python's documentation at http://www.python.org/doc/.
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
32 Installing IPython itself
33 =========================
34
35 Given a properly built Python, the basic interactive IPython shell will work with no external dependencies. However, some Python distributions (particularly on Windows and OS X), don't come with a working :mod:`readline` module. The IPython shell will work without :mod:`readline`, but will lack many features that users depend on, such as tab completion and command line editing. See below for details of how to make sure you have a working :mod:`readline`.
36
37 Installation using easy_install
38 -------------------------------
39
40 If you have :mod:`setuptools` installed, the easiest way of getting IPython is to simple use :command:`easy_install`::
41
42 $ easy_install ipython
43
44 That's it.
45
46 Installation from source
47 ------------------------
48
49 If you don't want to use :command:`easy_install`, or don't have it installed, just grab the latest stable build of IPython from `here <http://ipython.scipy.org/dist/>`_. Then do the following::
50
51 $ tar -xzf ipython.tar.gz
52 $ cd ipython
53 $ python setup.py install
54
55 If you are installing to a location (like ``/usr/local``) that requires higher permissions, you may need to run the last command with :command:`sudo`.
56
57 Windows
58 -------
59
60 There are a few caveats for Windows users. The main issue is that a basic ``python setup.py install`` approach won't create ``.bat`` file or Start Menu shortcuts, which most users want. To get an installation with these, there are two choices:
61
62 1. Install using :command:`easy_install`.
63
64 2. Install using our binary ``.exe`` Windows installer, which can be found at `here <http://ipython.scipy.org/dist/>`_
65
66 3. Install from source, but using :mod:`setuptools` (``python setupegg.py install``).
67
68 Installing the development version
69 ----------------------------------
70
71 It is also possible to install the development version of IPython from our `Bazaar <http://bazaar-vcs.org/>`_ source code
72 repository. To do this you will need to have Bazaar installed on your system. Then just do::
73
74 $ bzr branch lp:ipython
75 $ cd ipython
76 $ python setup.py install
77
78 Again, this last step on Windows won't create ``.bat`` files or Start Menu shortcuts, so you will have to use one of the other approaches listed above.
79
80 Some users want to be able to follow the development branch as it changes. If you have :mod:`setuptools` installed, this is easy. Simply replace the last step by::
81
82 $ python setupegg.py develop
83
84 This creates links in the right places and installs the command line script to the appropriate places. Then, if you want to update your IPython at any time, just do::
85
86 $ bzr pull
87
88 Basic optional dependencies
89 ===========================
90
91 There are a number of basic optional dependencies that most users will want to get. These are:
92
93 * readline (for command line editing, tab completion, etc.)
94 * nose (to run the IPython test suite)
95 * pexpect (to use things like irunner)
96
97 If you are comfortable installing these things yourself, have at it, otherwise read on for more details.
98
99 readline
100 --------
101
102 In principle, all Python distributions should come with a working :mod:`readline` module. But, reality is not quite that simple. There are two common situations where you won't have a working :mod:`readline` module:
103
104 * If you are using the built-in Python on Mac OS X.
105
106 * If you are running Windows, which doesn't have a :mod:`readline` module.
107
108 On OS X, the built-in Python doesn't not have :mod:`readline` because of license issues. Starting with OS X 10.5 (Leopard), Apple's built-in Python has a BSD-licensed not-quite-compatible readline replacement. As of IPython 0.9, many of the issues related to the differences between readline and libedit have been resolved. For many users, libedit may be sufficient.
109
110 Most users on OS X will want to get the full :mod:`readline` module. To get a working :mod:`readline` module, just do (with :mod:`setuptools` installed)::
111
112 $ easy_install readline
113
114 .. note:
115
116 Other Python distributions on OS X (such as fink, MacPorts and the
117 official python.org binaries) already have readline installed so
118 you don't have to do this step.
119
120 If needed, the readline egg can be build and installed from source (see the wiki page at http://ipython.scipy.org/moin/InstallationOSXLeopard).
121
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.
129
130 nose
131 ----
132
133 To run the IPython test suite you will need the :mod:`nose` package. Nose provides a great way of sniffing out and running all of the IPython tests. The simplest way of getting nose, is to use :command:`easy_install`::
134
135 $ easy_install nose
136
137 Another way of getting this is to do::
138
139 $ easy_install ipython[test]
140
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::
142
143 $ iptest
144
145
146 pexpect
147 -------
148
149 The `pexpect <http://www.noah.org/wiki/Pexpect>`_ package is used in IPython's :command:`irunner` script. On Unix platforms (including OS X), just do::
150
151 $ easy_install pexpect
152
153 Windows users are out of luck as pexpect does not run there.
154
155 Dependencies for IPython.kernel (parallel computing)
156 ====================================================
157
158 The IPython kernel provides a nice architecture for parallel computing. The main focus of this architecture is on interactive parallel computing. These features require a number of additional packages:
159
160 * zope.interface (yep, we use interfaces)
161 * Twisted (asynchronous networking framework)
162 * Foolscap (a nice, secure network protocol)
163 * pyOpenSSL (security for network connections)
164
165 On a Unix style platform (including OS X), if you want to use :mod:`setuptools`, you can just do::
166
167 $ easy_install ipython[kernel] # the first three
168 $ easy_install ipython[security] # pyOpenSSL
169
170 zope.interface and Twisted
171 --------------------------
172
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`::
176
177 $ easy_install zope.interface
178 $ easy_install Twisted
179
180 Of course, you can also download the source tarballs from the `Twisted website <twistedmatrix.org>`_ and the `zope.interface page at PyPI <http://pypi.python.org/pypi/zope.interface>`_ and do the usual ``python setup.py install`` if you prefer.
181
182 Windows is a bit different. For zope.interface and Twisted, simply get the latest binary ``.exe`` installer from the Twisted website. This installer includes both zope.interface and Twisted and should just work.
183
184 Foolscap
185 --------
186
187 Foolscap [Foolscap]_ uses Twisted to provide a very nice secure RPC protocol that we use to implement our parallel computing features.
188
189 On all platforms a simple::
190
191 $ easy_install foolscap
192
193 should work. You can also download the source tarballs from the `Foolscap website <http://foolscap.lothar.com/trac>`_ and do ``python setup.py install`` if you prefer.
194
195 pyOpenSSL
196 ---------
197
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:
199
200 1. Most Linux distributions have packages for pyOpenSSL.
201 2. The built-in Python 2.5 on OS X 10.5 already has it installed.
202 3. There are source tarballs on the pyOpenSSL website. On Unix-like
203 platforms, these can be built using ``python seutp.py install``.
204 4. There is also a binary ``.exe`` Windows installer on the `pyOpenSSL website <http://pyopenssl.sourceforge.net/>`_.
205
206 Dependencies for IPython.frontend (the IPython GUI)
207 ===================================================
208
209 wxPython
210 --------
211
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
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -26,6 +26,7 b' def make_color_table(in_class):'
26 Helper function for building the *TermColors classes."""
26 Helper function for building the *TermColors classes."""
27
27
28 color_templates = (
28 color_templates = (
29 # Dark colors
29 ("Black" , "0;30"),
30 ("Black" , "0;30"),
30 ("Red" , "0;31"),
31 ("Red" , "0;31"),
31 ("Green" , "0;32"),
32 ("Green" , "0;32"),
@@ -34,6 +35,7 b' def make_color_table(in_class):'
34 ("Purple" , "0;35"),
35 ("Purple" , "0;35"),
35 ("Cyan" , "0;36"),
36 ("Cyan" , "0;36"),
36 ("LightGray" , "0;37"),
37 ("LightGray" , "0;37"),
38 # Light colors
37 ("DarkGray" , "1;30"),
39 ("DarkGray" , "1;30"),
38 ("LightRed" , "1;31"),
40 ("LightRed" , "1;31"),
39 ("LightGreen" , "1;32"),
41 ("LightGreen" , "1;32"),
@@ -41,7 +43,17 b' def make_color_table(in_class):'
41 ("LightBlue" , "1;34"),
43 ("LightBlue" , "1;34"),
42 ("LightPurple" , "1;35"),
44 ("LightPurple" , "1;35"),
43 ("LightCyan" , "1;36"),
45 ("LightCyan" , "1;36"),
44 ("White" , "1;37"), )
46 ("White" , "1;37"),
47 # Blinking colors. Probably should not be used in anything serious.
48 ("BlinkBlack" , "5;30"),
49 ("BlinkRed" , "5;31"),
50 ("BlinkGreen" , "5;32"),
51 ("BlinkYellow" , "5;33"),
52 ("BlinkBlue" , "5;34"),
53 ("BlinkPurple" , "5;35"),
54 ("BlinkCyan" , "5;36"),
55 ("BlinkLightGray", "5;37"),
56 )
45
57
46 for name,value in color_templates:
58 for name,value in color_templates:
47 setattr(in_class,name,in_class._base % value)
59 setattr(in_class,name,in_class._base % value)
@@ -335,6 +335,12 b' def cd_completer(self, event):'
335 if not found:
335 if not found:
336 if os.path.isdir(relpath):
336 if os.path.isdir(relpath):
337 return [relpath]
337 return [relpath]
338 # if no completions so far, try bookmarks
339 bks = self.db.get('bookmarks',{}).keys()
340 bkmatches = [s for s in bks if s.startswith(event.symbol)]
341 if bkmatches:
342 return bkmatches
343
338 raise IPython.ipapi.TryNext
344 raise IPython.ipapi.TryNext
339
345
340
346
@@ -28,7 +28,8 b' def install_editor(run_template, wait = False):'
28 line = 0
28 line = 0
29 cmd = itplns(run_template, locals())
29 cmd = itplns(run_template, locals())
30 print ">",cmd
30 print ">",cmd
31 os.system(cmd)
31 if os.system(cmd) != 0:
32 raise IPython.ipapi.TryNext()
32 if wait:
33 if wait:
33 raw_input("Press Enter when done editing:")
34 raw_input("Press Enter when done editing:")
34
35
@@ -64,7 +65,10 b' def idle(exe = None):'
64 p = os.path.dirname(idlelib.__file__)
65 p = os.path.dirname(idlelib.__file__)
65 exe = p + '/idle.py'
66 exe = p + '/idle.py'
66 install_editor(exe + ' "$file"')
67 install_editor(exe + ' "$file"')
67
68
69 def mate(exe = 'mate'):
70 """ TextMate, the missing editor"""
71 install_editor(exe + ' -w -l $line "$file"')
68
72
69 # these are untested, report any problems
73 # these are untested, report any problems
70
74
@@ -8,7 +8,7 b' compatibility)'
8 """
8 """
9
9
10 from IPython import ipapi
10 from IPython import ipapi
11 import os,textwrap
11 import os,re,textwrap
12
12
13 # The import below effectively obsoletes your old-style ipythonrc[.ini],
13 # The import below effectively obsoletes your old-style ipythonrc[.ini],
14 # so consider yourself warned!
14 # so consider yourself warned!
@@ -50,9 +50,15 b' def main():'
50 ip.ex('import os')
50 ip.ex('import os')
51 ip.ex("def up(): os.chdir('..')")
51 ip.ex("def up(): os.chdir('..')")
52 ip.user_ns['LA'] = LastArgFinder()
52 ip.user_ns['LA'] = LastArgFinder()
53 # Nice prompt
54
53
55 o.prompt_in1= r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
54 # You can assign to _prompt_title variable
55 # to provide some extra information for prompt
56 # (e.g. the current mode, host/username...)
57
58 ip.user_ns['_prompt_title'] = ''
59
60 # Nice prompt
61 o.prompt_in1= r'\C_Green${_prompt_title}\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
56 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
62 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
57 o.prompt_out= '<\#> '
63 o.prompt_out= '<\#> '
58
64
@@ -98,9 +104,15 b' def main():'
98 for cmd in syscmds:
104 for cmd in syscmds:
99 # print "sys",cmd #dbg
105 # print "sys",cmd #dbg
100 noext, ext = os.path.splitext(cmd)
106 noext, ext = os.path.splitext(cmd)
101 key = mapper(noext)
107 if ext.lower() == '.exe':
108 cmd = noext
109
110 key = mapper(cmd)
102 if key not in ip.IP.alias_table:
111 if key not in ip.IP.alias_table:
103 ip.defalias(key, cmd)
112 # Dots will be removed from alias names, since ipython
113 # assumes names with dots to be python code
114
115 ip.defalias(key.replace('.',''), cmd)
104
116
105 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
117 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
106 ip.load("IPython.external.mglob")
118 ip.load("IPython.external.mglob")
@@ -117,7 +129,7 b' def main():'
117 # and the next best thing to real 'ls -F'
129 # and the next best thing to real 'ls -F'
118 ip.defalias('d','dir /w /og /on')
130 ip.defalias('d','dir /w /og /on')
119
131
120 ip.set_hook('input_prefilter', dotslash_prefilter_f)
132 ip.set_hook('input_prefilter', slash_prefilter_f)
121 extend_shell_behavior(ip)
133 extend_shell_behavior(ip)
122
134
123 class LastArgFinder:
135 class LastArgFinder:
@@ -139,13 +151,13 b' class LastArgFinder:'
139 return parts[-1]
151 return parts[-1]
140 return ""
152 return ""
141
153
142 def dotslash_prefilter_f(self,line):
154 def slash_prefilter_f(self,line):
143 """ ./foo now runs foo as system command
155 """ ./foo, ~/foo and /bin/foo now run foo as system command
144
156
145 Removes the need for doing !./foo
157 Removes the need for doing !./foo, !~/foo or !/bin/foo
146 """
158 """
147 import IPython.genutils
159 import IPython.genutils
148 if line.startswith("./"):
160 if re.match('(?:[.~]|/[a-zA-Z_0-9]+)/', line):
149 return "_ip.system(" + IPython.genutils.make_quoted_expr(line)+")"
161 return "_ip.system(" + IPython.genutils.make_quoted_expr(line)+")"
150 raise ipapi.TryNext
162 raise ipapi.TryNext
151
163
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -422,7 +422,11 b' python-profiler package from non-free.""")'
422 else:
422 else:
423 fndoc = 'No documentation'
423 fndoc = 'No documentation'
424 else:
424 else:
425 fndoc = fn.__doc__.rstrip()
425 if fn.__doc__:
426 fndoc = fn.__doc__.rstrip()
427 else:
428 fndoc = 'No documentation'
429
426
430
427 if mode == 'rest':
431 if mode == 'rest':
428 rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(self.shell.ESC_MAGIC,
432 rest_docs.append('**%s%s**::\n\n\t%s\n\n' %(self.shell.ESC_MAGIC,
@@ -2328,7 +2332,17 b' Currently the magic system has the following functions:\\n"""'
2328 # do actual editing here
2332 # do actual editing here
2329 print 'Editing...',
2333 print 'Editing...',
2330 sys.stdout.flush()
2334 sys.stdout.flush()
2331 self.shell.hooks.editor(filename,lineno)
2335 try:
2336 self.shell.hooks.editor(filename,lineno)
2337 except IPython.ipapi.TryNext:
2338 warn('Could not open editor')
2339 return
2340
2341 # XXX TODO: should this be generalized for all string vars?
2342 # For now, this is special-cased to blocks created by cpaste
2343 if args.strip() == 'pasted_block':
2344 self.shell.user_ns['pasted_block'] = file_read(filename)
2345
2332 if opts.has_key('x'): # -x prevents actual execution
2346 if opts.has_key('x'): # -x prevents actual execution
2333 print
2347 print
2334 else:
2348 else:
@@ -2338,6 +2352,8 b' Currently the magic system has the following functions:\\n"""'
2338 else:
2352 else:
2339 self.shell.safe_execfile(filename,self.shell.user_ns,
2353 self.shell.safe_execfile(filename,self.shell.user_ns,
2340 self.shell.user_ns)
2354 self.shell.user_ns)
2355
2356
2341 if use_temp:
2357 if use_temp:
2342 try:
2358 try:
2343 return open(filename).read()
2359 return open(filename).read()
@@ -2647,8 +2663,10 b' Defaulting color scheme to \'NoColor\'"""'
2647 if isexec(ff) and ff not in self.shell.no_alias:
2663 if isexec(ff) and ff not in self.shell.no_alias:
2648 # each entry in the alias table must be (N,name),
2664 # each entry in the alias table must be (N,name),
2649 # where N is the number of positional arguments of the
2665 # where N is the number of positional arguments of the
2650 # alias.
2666 # alias.
2651 alias_table[ff] = (0,ff)
2667 # Dots will be removed from alias names, since ipython
2668 # assumes names with dots to be python code
2669 alias_table[ff.replace('.','')] = (0,ff)
2652 syscmdlist.append(ff)
2670 syscmdlist.append(ff)
2653 else:
2671 else:
2654 for pdir in path:
2672 for pdir in path:
@@ -2658,7 +2676,7 b' Defaulting color scheme to \'NoColor\'"""'
2658 if isexec(ff) and base.lower() not in self.shell.no_alias:
2676 if isexec(ff) and base.lower() not in self.shell.no_alias:
2659 if ext.lower() == '.exe':
2677 if ext.lower() == '.exe':
2660 ff = base
2678 ff = base
2661 alias_table[base.lower()] = (0,ff)
2679 alias_table[base.lower().replace('.','')] = (0,ff)
2662 syscmdlist.append(ff)
2680 syscmdlist.append(ff)
2663 # Make sure the alias table doesn't contain keywords or builtins
2681 # Make sure the alias table doesn't contain keywords or builtins
2664 self.shell.alias_table_validate()
2682 self.shell.alias_table_validate()
@@ -3210,14 +3228,24 b' Defaulting color scheme to \'NoColor\'"""'
3210 This assigns the pasted block to variable 'foo' as string, without
3228 This assigns the pasted block to variable 'foo' as string, without
3211 dedenting or executing it (preceding >>> and + is still stripped)
3229 dedenting or executing it (preceding >>> and + is still stripped)
3212
3230
3231 '%cpaste -r' re-executes the block previously entered by cpaste.
3232
3213 Do not be alarmed by garbled output on Windows (it's a readline bug).
3233 Do not be alarmed by garbled output on Windows (it's a readline bug).
3214 Just press enter and type -- (and press enter again) and the block
3234 Just press enter and type -- (and press enter again) and the block
3215 will be what was just pasted.
3235 will be what was just pasted.
3216
3236
3217 IPython statements (magics, shell escapes) are not supported (yet).
3237 IPython statements (magics, shell escapes) are not supported (yet).
3218 """
3238 """
3219 opts,args = self.parse_options(parameter_s,'s:',mode='string')
3239 opts,args = self.parse_options(parameter_s,'rs:',mode='string')
3220 par = args.strip()
3240 par = args.strip()
3241 if opts.has_key('r'):
3242 b = self.user_ns.get('pasted_block', None)
3243 if b is None:
3244 raise UsageError('No previous pasted block available')
3245 print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b))
3246 exec b in self.user_ns
3247 return
3248
3221 sentinel = opts.get('s','--')
3249 sentinel = opts.get('s','--')
3222
3250
3223 # Regular expressions that declare text we strip from the input:
3251 # Regular expressions that declare text we strip from the input:
@@ -3245,8 +3273,8 b' Defaulting color scheme to \'NoColor\'"""'
3245 #print "block:\n",block
3273 #print "block:\n",block
3246 if not par:
3274 if not par:
3247 b = textwrap.dedent(block)
3275 b = textwrap.dedent(block)
3248 exec b in self.user_ns
3249 self.user_ns['pasted_block'] = b
3276 self.user_ns['pasted_block'] = b
3277 exec b in self.user_ns
3250 else:
3278 else:
3251 self.user_ns[par] = SList(block.splitlines())
3279 self.user_ns[par] = SList(block.splitlines())
3252 print "Block assigned to '%s'" % par
3280 print "Block assigned to '%s'" % par
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -1,7 +1,5 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Release data for the IPython project.
2 """Release data for the IPython project."""
3
4 $Id: Release.py 3002 2008-02-01 07:17:00Z fperez $"""
5
3
6 #*****************************************************************************
4 #*****************************************************************************
7 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
5 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
@@ -23,9 +21,9 b" name = 'ipython'"
23 # bdist_deb does not accept underscores (a Debian convention).
21 # bdist_deb does not accept underscores (a Debian convention).
24
22
25 development = False # change this to False to do a release
23 development = False # change this to False to do a release
26 version_base = '0.9.beta'
24 version_base = '0.9.1'
27 branch = 'ipython'
25 branch = 'ipython'
28 revision = '1099'
26 revision = '1143'
29
27
30 if development:
28 if development:
31 if branch == 'ipython':
29 if branch == 'ipython':
@@ -36,45 +34,69 b' else:'
36 version = version_base
34 version = version_base
37
35
38
36
39 description = "Tools for interactive development in Python."
37 description = "An interactive computing environment for Python"
40
38
41 long_description = \
39 long_description = \
42 """
40 """
43 IPython provides a replacement for the interactive Python interpreter with
41 The goal of IPython is to create a comprehensive environment for
44 extra functionality.
42 interactive and exploratory computing. To support this goal, IPython
43 has two main components:
44
45 * An enhanced interactive Python shell.
46
47 * An architecture for interactive parallel computing.
48
49 The enhanced interactive Python shell has the following main features:
50
51 * Comprehensive object introspection.
52
53 * Input history, persistent across sessions.
45
54
46 Main features:
55 * Caching of output results during a session with automatically generated
56 references.
47
57
48 * Comprehensive object introspection.
58 * Readline based name completion.
49
59
50 * Input history, persistent across sessions.
60 * Extensible system of 'magic' commands for controlling the environment and
61 performing many tasks related either to IPython or the operating system.
51
62
52 * Caching of output results during a session with automatically generated
63 * Configuration system with easy switching between different setups (simpler
53 references.
64 than changing $PYTHONSTARTUP environment variables every time).
54
65
55 * Readline based name completion.
66 * Session logging and reloading.
56
67
57 * Extensible system of 'magic' commands for controlling the environment and
68 * Extensible syntax processing for special purpose situations.
58 performing many tasks related either to IPython or the operating system.
59
69
60 * Configuration system with easy switching between different setups (simpler
70 * Access to the system shell with user-extensible alias system.
61 than changing $PYTHONSTARTUP environment variables every time).
62
71
63 * Session logging and reloading.
72 * Easily embeddable in other Python programs and wxPython GUIs.
64
73
65 * Extensible syntax processing for special purpose situations.
74 * Integrated access to the pdb debugger and the Python profiler.
66
75
67 * Access to the system shell with user-extensible alias system.
76 The parallel computing architecture has the following main features:
68
77
69 * Easily embeddable in other Python programs.
78 * Quickly parallelize Python code from an interactive Python/IPython session.
70
79
71 * Integrated access to the pdb debugger and the Python profiler.
80 * A flexible and dynamic process model that be deployed on anything from
81 multicore workstations to supercomputers.
72
82
73 The latest development version is always available at the IPython subversion
83 * An architecture that supports many different styles of parallelism, from
74 repository_.
84 message passing to task farming.
75
85
76 .. _repository: http://ipython.scipy.org/svn/ipython/ipython/trunk#egg=ipython-dev
86 * Both blocking and fully asynchronous interfaces.
77 """
87
88 * High level APIs that enable many things to be parallelized in a few lines
89 of code.
90
91 * Share live parallel jobs with other users securely.
92
93 * Dynamically load balanced task farming system.
94
95 * Robust error handling in parallel code.
96
97 The latest development version is always available from IPython's `Launchpad
98 site <http://launchpad.net/ipython>`_.
99 """
78
100
79 license = 'BSD'
101 license = 'BSD'
80
102
@@ -40,11 +40,12 b' except ImportError:'
40 # IPython imports
40 # IPython imports
41 import IPython
41 import IPython
42 from IPython import ultraTB, ipapi
42 from IPython import ultraTB, ipapi
43 from IPython.Magic import Magic
43 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 from IPython.iplib import InteractiveShell
45 from IPython.iplib import InteractiveShell
45 from IPython.ipmaker import make_IPython
46 from IPython.ipmaker import make_IPython
46 from IPython.Magic import Magic
47 from IPython.ipstruct import Struct
47 from IPython.ipstruct import Struct
48 from IPython.testing import decorators as testdec
48
49
49 # Globals
50 # Globals
50 # global flag to pass around information about Ctrl-C without exceptions
51 # global flag to pass around information about Ctrl-C without exceptions
@@ -384,7 +385,7 b' class MTInteractiveShell(InteractiveShell):'
384
385
385 Modified version of code.py's runsource(), to handle threading issues.
386 Modified version of code.py's runsource(), to handle threading issues.
386 See the original for full docstring details."""
387 See the original for full docstring details."""
387
388
388 global KBINT
389 global KBINT
389
390
390 # If Ctrl-C was typed, we reset the flag and return right away
391 # If Ctrl-C was typed, we reset the flag and return right away
@@ -414,7 +415,7 b' class MTInteractiveShell(InteractiveShell):'
414 if (self.worker_ident is None
415 if (self.worker_ident is None
415 or self.worker_ident == thread.get_ident() ):
416 or self.worker_ident == thread.get_ident() ):
416 InteractiveShell.runcode(self,code)
417 InteractiveShell.runcode(self,code)
417 return
418 return False
418
419
419 # Case 3
420 # Case 3
420 # Store code in queue, so the execution thread can handle it.
421 # Store code in queue, so the execution thread can handle it.
@@ -607,7 +608,8 b' class MatplotlibShellBase:'
607 # if a backend switch was performed, reverse it now
608 # if a backend switch was performed, reverse it now
608 if self.mpl_use._called:
609 if self.mpl_use._called:
609 self.matplotlib.rcParams['backend'] = self.mpl_backend
610 self.matplotlib.rcParams['backend'] = self.mpl_backend
610
611
612 @testdec.skip_doctest
611 def magic_run(self,parameter_s=''):
613 def magic_run(self,parameter_s=''):
612 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
614 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
613
615
@@ -774,6 +776,17 b' class IPShellGTK(IPThread):'
774 debug=1,shell_class=MTInteractiveShell):
776 debug=1,shell_class=MTInteractiveShell):
775
777
776 import gtk
778 import gtk
779 # Check for set_interactive, coming up in new pygtk.
780 # Disable it so that this code works, but notify
781 # the user that he has a better option as well.
782 # XXX TODO better support when set_interactive is released
783 try:
784 gtk.set_interactive(False)
785 print "Your PyGtk has set_interactive(), so you can use the"
786 print "more stable single-threaded Gtk mode."
787 print "See https://bugs.launchpad.net/ipython/+bug/270856"
788 except AttributeError:
789 pass
777
790
778 self.gtk = gtk
791 self.gtk = gtk
779 self.gtk_mainloop = hijack_gtk()
792 self.gtk_mainloop = hijack_gtk()
@@ -38,6 +38,8 b' ececute rad = pi/180.'
38 execute print '*** q is an alias for PhysicalQuantityInteractive'
38 execute print '*** q is an alias for PhysicalQuantityInteractive'
39 execute print '*** g = 9.8 m/s^2 has been defined'
39 execute print '*** g = 9.8 m/s^2 has been defined'
40 execute print '*** rad = pi/180 has been defined'
40 execute print '*** rad = pi/180 has been defined'
41 execute import ipy_constants as C
42 execute print '*** C is the physical constants module'
41
43
42 # Files to execute
44 # Files to execute
43 execfile
45 execfile
@@ -314,7 +314,10 b' class IPCompleter(Completer):'
314 # don't want to treat as delimiters in filename matching
314 # don't want to treat as delimiters in filename matching
315 # when escaped with backslash
315 # when escaped with backslash
316
316
317 protectables = ' '
317 if sys.platform == 'win32':
318 protectables = ' '
319 else:
320 protectables = ' ()'
318
321
319 if text.startswith('!'):
322 if text.startswith('!'):
320 text = text[1:]
323 text = text[1:]
@@ -16,13 +16,11 b' __docformat__ = "restructuredtext en"'
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 import os
18 import os
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
19 from os.path import join as pjoin
20
21 from IPython.genutils import get_home_dir, get_ipython_dir
20 from IPython.external.configobj import ConfigObj
22 from IPython.external.configobj import ConfigObj
21
23
22 # Traitlets config imports
23 from IPython.config import traitlets
24 from IPython.config.config import *
25 from traitlets import *
26
24
27 class ConfigObjManager(object):
25 class ConfigObjManager(object):
28
26
@@ -53,7 +51,7 b' class ConfigObjManager(object):'
53
51
54 def write_default_config_file(self):
52 def write_default_config_file(self):
55 ipdir = get_ipython_dir()
53 ipdir = get_ipython_dir()
56 fname = ipdir + '/' + self.filename
54 fname = pjoin(ipdir, self.filename)
57 if not os.path.isfile(fname):
55 if not os.path.isfile(fname):
58 print "Writing the configuration file to: " + fname
56 print "Writing the configuration file to: " + fname
59 self.write_config_obj_to_file(fname)
57 self.write_config_obj_to_file(fname)
@@ -87,11 +85,11 b' class ConfigObjManager(object):'
87
85
88 # In ipythondir if it is set
86 # In ipythondir if it is set
89 if ipythondir is not None:
87 if ipythondir is not None:
90 trythis = ipythondir + '/' + filename
88 trythis = pjoin(ipythondir, filename)
91 if os.path.isfile(trythis):
89 if os.path.isfile(trythis):
92 return trythis
90 return trythis
93
91
94 trythis = get_ipython_dir() + '/' + filename
92 trythis = pjoin(get_ipython_dir(), filename)
95 if os.path.isfile(trythis):
93 if os.path.isfile(trythis):
96 return trythis
94 return trythis
97
95
@@ -22,71 +22,6 b' import sys'
22 # Normal code begins
22 # Normal code begins
23 #---------------------------------------------------------------------------
23 #---------------------------------------------------------------------------
24
24
25 class HomeDirError(Exception):
26 pass
27
28 def get_home_dir():
29 """Return the closest possible equivalent to a 'home' directory.
30
31 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
32
33 Currently only Posix and NT are implemented, a HomeDirError exception is
34 raised for all other OSes. """
35
36 isdir = os.path.isdir
37 env = os.environ
38 try:
39 homedir = env['HOME']
40 if not isdir(homedir):
41 # in case a user stuck some string which does NOT resolve to a
42 # valid path, it's as good as if we hadn't foud it
43 raise KeyError
44 return homedir
45 except KeyError:
46 if os.name == 'posix':
47 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
48 elif os.name == 'nt':
49 # For some strange reason, win9x returns 'nt' for os.name.
50 try:
51 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
52 if not isdir(homedir):
53 homedir = os.path.join(env['USERPROFILE'])
54 if not isdir(homedir):
55 raise HomeDirError
56 return homedir
57 except:
58 try:
59 # Use the registry to get the 'My Documents' folder.
60 import _winreg as wreg
61 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
62 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
63 homedir = wreg.QueryValueEx(key,'Personal')[0]
64 key.Close()
65 if not isdir(homedir):
66 e = ('Invalid "Personal" folder registry key '
67 'typically "My Documents".\n'
68 'Value: %s\n'
69 'This is not a valid directory on your system.' %
70 homedir)
71 raise HomeDirError(e)
72 return homedir
73 except HomeDirError:
74 raise
75 except:
76 return 'C:\\'
77 elif os.name == 'dos':
78 # Desperate, may do absurd things in classic MacOS. May work under DOS.
79 return 'C:\\'
80 else:
81 raise HomeDirError,'support for your operating system not implemented.'
82
83 def get_ipython_dir():
84 ipdir_def = '.ipython'
85 home_dir = get_home_dir()
86 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
87 os.path.join(home_dir,ipdir_def)))
88 return ipdir
89
90 def import_item(key):
25 def import_item(key):
91 """
26 """
92 Import and return bar given the string foo.bar.
27 Import and return bar given the string foo.bar.
@@ -180,7 +180,7 b' def re_mark(mark):'
180
180
181 class Demo(object):
181 class Demo(object):
182
182
183 re_stop = re_mark('-?\s?stop\s?-?')
183 re_stop = re_mark('-*\s?stop\s?-*')
184 re_silent = re_mark('silent')
184 re_silent = re_mark('silent')
185 re_auto = re_mark('auto')
185 re_auto = re_mark('auto')
186 re_auto_all = re_mark('auto_all')
186 re_auto_all = re_mark('auto_all')
@@ -50,7 +50,6 b' import subprocess'
50 from subprocess import PIPE
50 from subprocess import PIPE
51 import sys
51 import sys
52 import os
52 import os
53 import time
54 import types
53 import types
55
54
56 try:
55 try:
@@ -69,8 +68,16 b' except ImportError:'
69
68
70 mswindows = (sys.platform == "win32")
69 mswindows = (sys.platform == "win32")
71
70
71 skip = False
72
72 if mswindows:
73 if mswindows:
73 import winprocess
74 import platform
75 if platform.uname()[3] == '' or platform.uname()[3] > '6.0.6000':
76 # Killable process does not work under vista when starting for
77 # something else than cmd.
78 skip = True
79 else:
80 import winprocess
74 else:
81 else:
75 import signal
82 import signal
76
83
@@ -78,7 +85,11 b' if not mswindows:'
78 def DoNothing(*args):
85 def DoNothing(*args):
79 pass
86 pass
80
87
81 class Popen(subprocess.Popen):
88
89 if skip:
90 Popen = subprocess.Popen
91 else:
92 class Popen(subprocess.Popen):
82 if not mswindows:
93 if not mswindows:
83 # Override __init__ to set a preexec_fn
94 # Override __init__ to set a preexec_fn
84 def __init__(self, *args, **kwargs):
95 def __init__(self, *args, **kwargs):
@@ -50,7 +50,7 b' class PipedProcess(Thread):'
50 """
50 """
51 env = os.environ
51 env = os.environ
52 env['TERM'] = 'xterm'
52 env['TERM'] = 'xterm'
53 process = Popen((self.command_string + ' 2>&1', ), shell=True,
53 process = Popen(self.command_string + ' 2>&1', shell=True,
54 env=env,
54 env=env,
55 universal_newlines=True,
55 universal_newlines=True,
56 stdout=PIPE, stdin=PIPE, )
56 stdout=PIPE, stdin=PIPE, )
@@ -14,30 +14,14 b' __docformat__ = "restructuredtext en"'
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 import uuid
17 from IPython.external import guid
18
18
19 try:
20 from zope.interface import Interface, Attribute, implements, classProvides
21 except ImportError, e:
22 e.message = """%s
23 ________________________________________________________________________________
24 zope.interface is required to run asynchronous frontends.""" % e.message
25 e.args = (e.message, ) + e.args[1:]
26
19
27 from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
20 from zope.interface import Interface, Attribute, implements, classProvides
28
21 from twisted.python.failure import Failure
29 from IPython.kernel.engineservice import IEngineCore
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
30 from IPython.kernel.core.history import FrontEndHistory
23 from IPython.kernel.core.history import FrontEndHistory
31
24 from IPython.kernel.engineservice import IEngineCore
32 try:
33 from twisted.python.failure import Failure
34 except ImportError, e:
35 e.message = """%s
36 ________________________________________________________________________________
37 twisted is required to run asynchronous frontends.""" % e.message
38 e.args = (e.message, ) + e.args[1:]
39
40
41
25
42
26
43 class AsyncFrontEndBase(FrontEndBase):
27 class AsyncFrontEndBase(FrontEndBase):
@@ -75,7 +59,7 b' class AsyncFrontEndBase(FrontEndBase):'
75 return Failure(Exception("Block is not compilable"))
59 return Failure(Exception("Block is not compilable"))
76
60
77 if(blockID == None):
61 if(blockID == None):
78 blockID = uuid.uuid4() #random UUID
62 blockID = guid.generate()
79
63
80 d = self.engine.execute(block)
64 d = self.engine.execute(block)
81 d.addCallback(self._add_history, block=block)
65 d.addCallback(self._add_history, block=block)
@@ -26,7 +26,7 b' __docformat__ = "restructuredtext en"'
26
26
27 import sys
27 import sys
28 import objc
28 import objc
29 import uuid
29 from IPython.external import guid
30
30
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
@@ -361,7 +361,7 b' class IPythonCocoaController(NSObject, AsyncFrontEndBase):'
361
361
362 def next_block_ID(self):
362 def next_block_ID(self):
363
363
364 return uuid.uuid4()
364 return guid.generate()
365
365
366 def new_cell_block(self):
366 def new_cell_block(self):
367 """A new CellBlock at the end of self.textView.textStorage()"""
367 """A new CellBlock at the end of self.textView.textStorage()"""
@@ -14,28 +14,31 b' __docformat__ = "restructuredtext en"'
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17 from IPython.kernel.core.interpreter import Interpreter
17
18 import IPython.kernel.engineservice as es
18 try:
19 from IPython.testing.util import DeferredTestCase
19 from IPython.kernel.core.interpreter import Interpreter
20 from twisted.internet.defer import succeed
20 import IPython.kernel.engineservice as es
21 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
21 from IPython.testing.util import DeferredTestCase
22
22 from twisted.internet.defer import succeed
23 from Foundation import NSMakeRect
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 from AppKit import NSTextView, NSScrollView
24 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
26 except ImportError:
27 import nose
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
25
29
26 class TestIPythonCocoaControler(DeferredTestCase):
30 class TestIPythonCocoaControler(DeferredTestCase):
27 """Tests for IPythonCocoaController"""
31 """Tests for IPythonCocoaController"""
28
32
29 def setUp(self):
33 def setUp(self):
30 self.controller = IPythonCocoaController.alloc().init()
34 self.controller = IPythonCocoaController.alloc().init()
31 self.engine = es.EngineService()
35 self.engine = es.EngineService()
32 self.engine.startService()
36 self.engine.startService()
33
37
34
35 def tearDown(self):
38 def tearDown(self):
36 self.controller = None
39 self.controller = None
37 self.engine.stopService()
40 self.engine.stopService()
38
41
39 def testControllerExecutesCode(self):
42 def testControllerExecutesCode(self):
40 code ="""5+5"""
43 code ="""5+5"""
41 expected = Interpreter().execute(code)
44 expected = Interpreter().execute(code)
@@ -47,45 +50,45 b' class TestIPythonCocoaControler(DeferredTestCase):'
47 self.assertDeferredEquals(
50 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
51 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
52 expected)
50
53
51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
54 def testControllerMirrorsUserNSWithValuesAsStrings(self):
52 code = """userns1=1;userns2=2"""
55 code = """userns1=1;userns2=2"""
53 def testControllerUserNS(result):
56 def testControllerUserNS(result):
54 self.assertEquals(self.controller.userNS['userns1'], 1)
57 self.assertEquals(self.controller.userNS['userns1'], 1)
55 self.assertEquals(self.controller.userNS['userns2'], 2)
58 self.assertEquals(self.controller.userNS['userns2'], 2)
56
59
57 self.controller.execute(code).addCallback(testControllerUserNS)
60 self.controller.execute(code).addCallback(testControllerUserNS)
58
61
59
62
60 def testControllerInstantiatesIEngine(self):
63 def testControllerInstantiatesIEngine(self):
61 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
64 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
62
65
63 def testControllerCompletesToken(self):
66 def testControllerCompletesToken(self):
64 code = """longNameVariable=10"""
67 code = """longNameVariable=10"""
65 def testCompletes(result):
68 def testCompletes(result):
66 self.assert_("longNameVariable" in result)
69 self.assert_("longNameVariable" in result)
67
70
68 def testCompleteToken(result):
71 def testCompleteToken(result):
69 self.controller.complete("longNa").addCallback(testCompletes)
72 self.controller.complete("longNa").addCallback(testCompletes)
70
73
71 self.controller.execute(code).addCallback(testCompletes)
74 self.controller.execute(code).addCallback(testCompletes)
72
75
73
76
74 def testCurrentIndent(self):
77 def testCurrentIndent(self):
75 """test that current_indent_string returns current indent or None.
78 """test that current_indent_string returns current indent or None.
76 Uses _indent_for_block for direct unit testing.
79 Uses _indent_for_block for direct unit testing.
77 """
80 """
78
81
79 self.controller.tabUsesSpaces = True
82 self.controller.tabUsesSpaces = True
80 self.assert_(self.controller._indent_for_block("""a=3""") == None)
83 self.assert_(self.controller._indent_for_block("""a=3""") == None)
81 self.assert_(self.controller._indent_for_block("") == None)
84 self.assert_(self.controller._indent_for_block("") == None)
82 block = """def test():\n a=3"""
85 block = """def test():\n a=3"""
83 self.assert_(self.controller._indent_for_block(block) == \
86 self.assert_(self.controller._indent_for_block(block) == \
84 ' ' * self.controller.tabSpaces)
87 ' ' * self.controller.tabSpaces)
85
88
86 block = """if(True):\n%sif(False):\n%spass""" % \
89 block = """if(True):\n%sif(False):\n%spass""" % \
87 (' '*self.controller.tabSpaces,
90 (' '*self.controller.tabSpaces,
88 2*' '*self.controller.tabSpaces)
91 2*' '*self.controller.tabSpaces)
89 self.assert_(self.controller._indent_for_block(block) == \
92 self.assert_(self.controller._indent_for_block(block) == \
90 2*(' '*self.controller.tabSpaces))
93 2*(' '*self.controller.tabSpaces))
91
94
@@ -21,14 +21,16 b' __docformat__ = "restructuredtext en"'
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import string
23 import string
24 import uuid
24 import codeop
25 import _ast
25 from IPython.external import guid
26
26
27 from zopeinterface import Interface, Attribute, implements, classProvides
28
27
28 from IPython.frontend.zopeinterface import (
29 Interface,
30 Attribute,
31 )
29 from IPython.kernel.core.history import FrontEndHistory
32 from IPython.kernel.core.history import FrontEndHistory
30 from IPython.kernel.core.util import Bunch
33 from IPython.kernel.core.util import Bunch
31 from IPython.kernel.engineservice import IEngineCore
32
34
33 ##############################################################################
35 ##############################################################################
34 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
36 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
@@ -131,11 +133,7 b' class IFrontEnd(Interface):'
131
133
132 pass
134 pass
133
135
134 def compile_ast(block):
136
135 """Compiles block to an _ast.AST"""
136
137 pass
138
139 def get_history_previous(current_block):
137 def get_history_previous(current_block):
140 """Returns the block previous in the history. Saves currentBlock if
138 """Returns the block previous in the history. Saves currentBlock if
141 the history_cursor is currently at the end of the input history"""
139 the history_cursor is currently at the end of the input history"""
@@ -217,28 +215,14 b' class FrontEndBase(object):'
217 """
215 """
218
216
219 try:
217 try:
220 ast = self.compile_ast(block)
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
219 "<string>", "exec")
221 except:
220 except:
222 return False
221 return False
223
222
224 lines = block.split('\n')
223 lines = block.split('\n')
225 return (len(lines)==1 or str(lines[-1])=='')
224 return ((is_complete is not None)
226
225 and (len(lines)==1 or str(lines[-1])==''))
227
228 def compile_ast(self, block):
229 """Compile block to an AST
230
231 Parameters:
232 block : str
233
234 Result:
235 AST
236
237 Throws:
238 Exception if block cannot be compiled
239 """
240
241 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
242
226
243
227
244 def execute(self, block, blockID=None):
228 def execute(self, block, blockID=None):
@@ -258,7 +242,7 b' class FrontEndBase(object):'
258 raise Exception("Block is not compilable")
242 raise Exception("Block is not compilable")
259
243
260 if(blockID == None):
244 if(blockID == None):
261 blockID = uuid.uuid4() #random UUID
245 blockID = guid.generate()
262
246
263 try:
247 try:
264 result = self.shell.execute(block)
248 result = self.shell.execute(block)
@@ -20,6 +20,8 b' import re'
20
20
21 import IPython
21 import IPython
22 import sys
22 import sys
23 import codeop
24 import traceback
23
25
24 from frontendbase import FrontEndBase
26 from frontendbase import FrontEndBase
25 from IPython.kernel.core.interpreter import Interpreter
27 from IPython.kernel.core.interpreter import Interpreter
@@ -76,6 +78,11 b' class LineFrontEndBase(FrontEndBase):'
76
78
77 if banner is not None:
79 if banner is not None:
78 self.banner = banner
80 self.banner = banner
81
82 def start(self):
83 """ Put the frontend in a state where it is ready for user
84 interaction.
85 """
79 if self.banner is not None:
86 if self.banner is not None:
80 self.write(self.banner, refresh=False)
87 self.write(self.banner, refresh=False)
81
88
@@ -141,9 +148,18 b' class LineFrontEndBase(FrontEndBase):'
141 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
148 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
142 return False
149 return False
143 else:
150 else:
144 # Add line returns here, to make sure that the statement is
151 self.capture_output()
145 # complete.
152 try:
146 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
153 # Add line returns here, to make sure that the statement is
154 # complete.
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 "<string>", "exec")
157 self.release_output()
158 except Exception, e:
159 # XXX: Hack: return True so that the
160 # code gets executed and the error captured.
161 is_complete = True
162 return is_complete
147
163
148
164
149 def write(self, string, refresh=True):
165 def write(self, string, refresh=True):
@@ -166,22 +182,35 b' class LineFrontEndBase(FrontEndBase):'
166 raw_string = python_string
182 raw_string = python_string
167 # Create a false result, in case there is an exception
183 # Create a false result, in case there is an exception
168 self.last_result = dict(number=self.prompt_number)
184 self.last_result = dict(number=self.prompt_number)
185
186 ## try:
187 ## self.history.input_cache[-1] = raw_string.rstrip()
188 ## result = self.shell.execute(python_string)
189 ## self.last_result = result
190 ## self.render_result(result)
191 ## except:
192 ## self.show_traceback()
193 ## finally:
194 ## self.after_execute()
195
169 try:
196 try:
170 self.history.input_cache[-1] = raw_string.rstrip()
197 try:
171 result = self.shell.execute(python_string)
198 self.history.input_cache[-1] = raw_string.rstrip()
172 self.last_result = result
199 result = self.shell.execute(python_string)
173 self.render_result(result)
200 self.last_result = result
174 except:
201 self.render_result(result)
175 self.show_traceback()
202 except:
203 self.show_traceback()
176 finally:
204 finally:
177 self.after_execute()
205 self.after_execute()
178
206
207
179 #--------------------------------------------------------------------------
208 #--------------------------------------------------------------------------
180 # LineFrontEndBase interface
209 # LineFrontEndBase interface
181 #--------------------------------------------------------------------------
210 #--------------------------------------------------------------------------
182
211
183 def prefilter_input(self, string):
212 def prefilter_input(self, string):
184 """ Priflter the input to turn it in valid python.
213 """ Prefilter the input to turn it in valid python.
185 """
214 """
186 string = string.replace('\r\n', '\n')
215 string = string.replace('\r\n', '\n')
187 string = string.replace('\t', 4*' ')
216 string = string.replace('\t', 4*' ')
@@ -210,9 +239,12 b' class LineFrontEndBase(FrontEndBase):'
210 line = self.input_buffer
239 line = self.input_buffer
211 new_line, completions = self.complete(line)
240 new_line, completions = self.complete(line)
212 if len(completions)>1:
241 if len(completions)>1:
213 self.write_completion(completions)
242 self.write_completion(completions, new_line=new_line)
214 self.input_buffer = new_line
243 elif not line == new_line:
244 self.input_buffer = new_line
215 if self.debug:
245 if self.debug:
246 print >>sys.__stdout__, 'line', line
247 print >>sys.__stdout__, 'new_line', new_line
216 print >>sys.__stdout__, completions
248 print >>sys.__stdout__, completions
217
249
218
250
@@ -222,10 +254,15 b' class LineFrontEndBase(FrontEndBase):'
222 return 80
254 return 80
223
255
224
256
225 def write_completion(self, possibilities):
257 def write_completion(self, possibilities, new_line=None):
226 """ Write the list of possible completions.
258 """ Write the list of possible completions.
259
260 new_line is the completed input line that should be displayed
261 after the completion are writen. If None, the input_buffer
262 before the completion is used.
227 """
263 """
228 current_buffer = self.input_buffer
264 if new_line is None:
265 new_line = self.input_buffer
229
266
230 self.write('\n')
267 self.write('\n')
231 max_len = len(max(possibilities, key=len)) + 1
268 max_len = len(max(possibilities, key=len)) + 1
@@ -246,7 +283,7 b' class LineFrontEndBase(FrontEndBase):'
246 self.write(''.join(buf))
283 self.write(''.join(buf))
247 self.new_prompt(self.input_prompt_template.substitute(
284 self.new_prompt(self.input_prompt_template.substitute(
248 number=self.last_result['number'] + 1))
285 number=self.last_result['number'] + 1))
249 self.input_buffer = current_buffer
286 self.input_buffer = new_line
250
287
251
288
252 def new_prompt(self, prompt):
289 def new_prompt(self, prompt):
@@ -275,6 +312,8 b' class LineFrontEndBase(FrontEndBase):'
275 else:
312 else:
276 self.input_buffer += self._get_indent_string(
313 self.input_buffer += self._get_indent_string(
277 current_buffer[:-1])
314 current_buffer[:-1])
315 if len(current_buffer.split('\n')) == 2:
316 self.input_buffer += '\t\t'
278 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
279 self.input_buffer += '\t'
318 self.input_buffer += '\t'
280
319
@@ -24,6 +24,7 b' __docformat__ = "restructuredtext en"'
24 import sys
24 import sys
25
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
27
28
28 from IPython.ipmaker import make_IPython
29 from IPython.ipmaker import make_IPython
29 from IPython.ipapi import IPApi
30 from IPython.ipapi import IPApi
@@ -34,6 +35,7 b' from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap'
34 from IPython.genutils import Term
35 from IPython.genutils import Term
35 import pydoc
36 import pydoc
36 import os
37 import os
38 import sys
37
39
38
40
39 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
@@ -57,6 +59,8 b' class PrefilterFrontEnd(LineFrontEndBase):'
57 to execute the statements and the ipython0 used for code
59 to execute the statements and the ipython0 used for code
58 completion...
60 completion...
59 """
61 """
62
63 debug = False
60
64
61 def __init__(self, ipython0=None, *args, **kwargs):
65 def __init__(self, ipython0=None, *args, **kwargs):
62 """ Parameters:
66 """ Parameters:
@@ -65,12 +69,24 b' class PrefilterFrontEnd(LineFrontEndBase):'
65 ipython0: an optional ipython0 instance to use for command
69 ipython0: an optional ipython0 instance to use for command
66 prefiltering and completion.
70 prefiltering and completion.
67 """
71 """
72 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
75 err_callback=self.write,
76 )
77 self.shell.traceback_trap = SyncTracebackTrap(
78 formatters=self.shell.traceback_trap.formatters,
79 )
80
81 # Start the ipython0 instance:
68 self.save_output_hooks()
82 self.save_output_hooks()
69 if ipython0 is None:
83 if ipython0 is None:
70 # Instanciate an IPython0 interpreter to be able to use the
84 # Instanciate an IPython0 interpreter to be able to use the
71 # prefiltering.
85 # prefiltering.
72 # XXX: argv=[] is a bit bold.
86 # XXX: argv=[] is a bit bold.
73 ipython0 = make_IPython(argv=[])
87 ipython0 = make_IPython(argv=[],
88 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
74 self.ipython0 = ipython0
90 self.ipython0 = ipython0
75 # Set the pager:
91 # Set the pager:
76 self.ipython0.set_hook('show_in_pager',
92 self.ipython0.set_hook('show_in_pager',
@@ -86,24 +102,13 b' class PrefilterFrontEnd(LineFrontEndBase):'
86 'ls -CF')
102 'ls -CF')
87 # And now clean up the mess created by ipython0
103 # And now clean up the mess created by ipython0
88 self.release_output()
104 self.release_output()
105
106
89 if not 'banner' in kwargs and self.banner is None:
107 if not 'banner' in kwargs and self.banner is None:
90 kwargs['banner'] = self.ipython0.BANNER + """
108 self.banner = self.ipython0.BANNER + """
91 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
92
110
93 LineFrontEndBase.__init__(self, *args, **kwargs)
111 self.start()
94 # XXX: Hack: mix the two namespaces
95 self.shell.user_ns.update(self.ipython0.user_ns)
96 self.ipython0.user_ns = self.shell.user_ns
97 self.shell.user_global_ns.update(self.ipython0.user_global_ns)
98 self.ipython0.user_global_ns = self.shell.user_global_ns
99
100 self.shell.output_trap = RedirectorOutputTrap(
101 out_callback=self.write,
102 err_callback=self.write,
103 )
104 self.shell.traceback_trap = SyncTracebackTrap(
105 formatters=self.shell.traceback_trap.formatters,
106 )
107
112
108 #--------------------------------------------------------------------------
113 #--------------------------------------------------------------------------
109 # FrontEndBase interface
114 # FrontEndBase interface
@@ -113,7 +118,7 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
113 """ Use ipython0 to capture the last traceback and display it.
118 """ Use ipython0 to capture the last traceback and display it.
114 """
119 """
115 self.capture_output()
120 self.capture_output()
116 self.ipython0.showtraceback()
121 self.ipython0.showtraceback(tb_offset=-1)
117 self.release_output()
122 self.release_output()
118
123
119
124
@@ -164,6 +169,8 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
164
169
165
170
166 def complete(self, line):
171 def complete(self, line):
172 # FIXME: This should be factored out in the linefrontendbase
173 # method.
167 word = line.split('\n')[-1].split(' ')[-1]
174 word = line.split('\n')[-1].split(' ')[-1]
168 completions = self.ipython0.complete(word)
175 completions = self.ipython0.complete(word)
169 # FIXME: The proper sort should be done in the complete method.
176 # FIXME: The proper sort should be done in the complete method.
@@ -189,17 +196,33 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
189 # capture it.
196 # capture it.
190 self.capture_output()
197 self.capture_output()
191 self.last_result = dict(number=self.prompt_number)
198 self.last_result = dict(number=self.prompt_number)
199
200 ## try:
201 ## for line in input_string.split('\n'):
202 ## filtered_lines.append(
203 ## self.ipython0.prefilter(line, False).rstrip())
204 ## except:
205 ## # XXX: probably not the right thing to do.
206 ## self.ipython0.showsyntaxerror()
207 ## self.after_execute()
208 ## finally:
209 ## self.release_output()
210
211
192 try:
212 try:
193 for line in input_string.split('\n'):
213 try:
194 filtered_lines.append(
214 for line in input_string.split('\n'):
195 self.ipython0.prefilter(line, False).rstrip())
215 filtered_lines.append(
196 except:
216 self.ipython0.prefilter(line, False).rstrip())
197 # XXX: probably not the right thing to do.
217 except:
198 self.ipython0.showsyntaxerror()
218 # XXX: probably not the right thing to do.
199 self.after_execute()
219 self.ipython0.showsyntaxerror()
220 self.after_execute()
200 finally:
221 finally:
201 self.release_output()
222 self.release_output()
202
223
224
225
203 # Clean up the trailing whitespace, to avoid indentation errors
226 # Clean up the trailing whitespace, to avoid indentation errors
204 filtered_string = '\n'.join(filtered_lines)
227 filtered_string = '\n'.join(filtered_lines)
205 return filtered_string
228 return filtered_string
@@ -1,152 +1,32 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2 """
3 """This file contains unittests for the frontendbase module."""
3 Test the basic functionality of frontendbase.
4 """
4
5
5 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
6
7
7 #---------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
9 #
10 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is
11 # the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17
14
18 import unittest
15 from IPython.frontend.frontendbase import FrontEndBase
19 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
20 from IPython.frontend import frontendbase
21 from IPython.kernel.engineservice import EngineService
22
16
23 class FrontEndCallbackChecker(AsyncFrontEndBase):
17 def test_iscomplete():
24 """FrontEndBase subclass for checking callbacks"""
18 """ Check that is_complete works.
25 def __init__(self, engine=None, history=None):
19 """
26 super(FrontEndCallbackChecker, self).__init__(engine=engine,
20 f = FrontEndBase()
27 history=history)
21 assert f.is_complete('(a + a)')
28 self.updateCalled = False
22 assert not f.is_complete('(a + a')
29 self.renderResultCalled = False
23 assert f.is_complete('1')
30 self.renderErrorCalled = False
24 assert not f.is_complete('1 + ')
31
25 assert not f.is_complete('1 + \n\n')
32 def update_cell_prompt(self, result, blockID=None):
26 assert f.is_complete('if True:\n print 1\n')
33 self.updateCalled = True
27 assert not f.is_complete('if True:\n print 1')
34 return result
28 assert f.is_complete('def f():\n print 1\n')
35
36 def render_result(self, result):
37 self.renderResultCalled = True
38 return result
39
40
41 def render_error(self, failure):
42 self.renderErrorCalled = True
43 return failure
44
45
29
30 if __name__ == '__main__':
31 test_iscomplete()
46
32
47
48 class TestAsyncFrontendBase(unittest.TestCase):
49 def setUp(self):
50 """Setup the EngineService and FrontEndBase"""
51
52 self.fb = FrontEndCallbackChecker(engine=EngineService())
53
54
55 def test_implements_IFrontEnd(self):
56 assert(frontendbase.IFrontEnd.implementedBy(
57 AsyncFrontEndBase))
58
59
60 def test_is_complete_returns_False_for_incomplete_block(self):
61 """"""
62
63 block = """def test(a):"""
64
65 assert(self.fb.is_complete(block) == False)
66
67 def test_is_complete_returns_True_for_complete_block(self):
68 """"""
69
70 block = """def test(a): pass"""
71
72 assert(self.fb.is_complete(block))
73
74 block = """a=3"""
75
76 assert(self.fb.is_complete(block))
77
78
79 def test_blockID_added_to_result(self):
80 block = """3+3"""
81
82 d = self.fb.execute(block, blockID='TEST_ID')
83
84 d.addCallback(self.checkBlockID, expected='TEST_ID')
85
86 def test_blockID_added_to_failure(self):
87 block = "raise Exception()"
88
89 d = self.fb.execute(block,blockID='TEST_ID')
90 d.addErrback(self.checkFailureID, expected='TEST_ID')
91
92 def checkBlockID(self, result, expected=""):
93 assert(result['blockID'] == expected)
94
95
96 def checkFailureID(self, failure, expected=""):
97 assert(failure.blockID == expected)
98
99
100 def test_callbacks_added_to_execute(self):
101 """test that
102 update_cell_prompt
103 render_result
104
105 are added to execute request
106 """
107
108 d = self.fb.execute("10+10")
109 d.addCallback(self.checkCallbacks)
110
111
112 def checkCallbacks(self, result):
113 assert(self.fb.updateCalled)
114 assert(self.fb.renderResultCalled)
115
116
117 def test_error_callback_added_to_execute(self):
118 """test that render_error called on execution error"""
119
120 d = self.fb.execute("raise Exception()")
121 d.addCallback(self.checkRenderError)
122
123 def checkRenderError(self, result):
124 assert(self.fb.renderErrorCalled)
125
126 def test_history_returns_expected_block(self):
127 """Make sure history browsing doesn't fail"""
128
129 blocks = ["a=1","a=2","a=3"]
130 for b in blocks:
131 d = self.fb.execute(b)
132
133 # d is now the deferred for the last executed block
134 d.addCallback(self.historyTests, blocks)
135
136
137 def historyTests(self, result, blocks):
138 """historyTests"""
139
140 assert(len(blocks) >= 3)
141 assert(self.fb.get_history_previous("") == blocks[-2])
142 assert(self.fb.get_history_previous("") == blocks[-3])
143 assert(self.fb.get_history_next() == blocks[-2])
144
145
146 def test_history_returns_none_at_startup(self):
147 """test_history_returns_none_at_startup"""
148
149 assert(self.fb.get_history_previous("")==None)
150 assert(self.fb.get_history_next()==None)
151
152
@@ -17,6 +17,8 b' from time import sleep'
17 import sys
17 import sys
18
18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend._process import PipedProcess
20 from IPython.testing import decorators as testdec
21
20
22
21 def test_capture_out():
23 def test_capture_out():
22 """ A simple test to see if we can execute a process and get the output.
24 """ A simple test to see if we can execute a process and get the output.
@@ -25,7 +27,8 b' def test_capture_out():'
25 p = PipedProcess('echo 1', out_callback=s.write, )
27 p = PipedProcess('echo 1', out_callback=s.write, )
26 p.start()
28 p.start()
27 p.join()
29 p.join()
28 assert s.getvalue() == '1\n'
30 result = s.getvalue().rstrip()
31 assert result == '1'
29
32
30
33
31 def test_io():
34 def test_io():
@@ -40,7 +43,8 b' def test_io():'
40 sleep(0.1)
43 sleep(0.1)
41 p.process.stdin.write(test_string)
44 p.process.stdin.write(test_string)
42 p.join()
45 p.join()
43 assert s.getvalue() == test_string
46 result = s.getvalue()
47 assert result == test_string
44
48
45
49
46 def test_kill():
50 def test_kill():
@@ -23,6 +23,7 b' import wx'
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26 import time
26 import sys
27 import sys
27 LINESEP = '\n'
28 LINESEP = '\n'
28 if sys.platform == 'win32':
29 if sys.platform == 'win32':
@@ -35,7 +36,7 b' import re'
35
36
36 _DEFAULT_SIZE = 10
37 _DEFAULT_SIZE = 10
37 if sys.platform == 'darwin':
38 if sys.platform == 'darwin':
38 _DEFAULT_STYLE = 12
39 _DEFAULT_SIZE = 12
39
40
40 _DEFAULT_STYLE = {
41 _DEFAULT_STYLE = {
41 'stdout' : 'fore:#0000FF',
42 'stdout' : 'fore:#0000FF',
@@ -115,12 +116,15 b' class ConsoleWidget(editwindow.EditWindow):'
115 # The color of the carret (call _apply_style() after setting)
116 # The color of the carret (call _apply_style() after setting)
116 carret_color = 'BLACK'
117 carret_color = 'BLACK'
117
118
119 # Store the last time a refresh was done
120 _last_refresh_time = 0
121
118 #--------------------------------------------------------------------------
122 #--------------------------------------------------------------------------
119 # Public API
123 # Public API
120 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
121
125
122 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
123 size=wx.DefaultSize, style=0, ):
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
124 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
125 self._configure_scintilla()
129 self._configure_scintilla()
126
130
@@ -168,9 +172,14 b' class ConsoleWidget(editwindow.EditWindow):'
168
172
169 self.GotoPos(self.GetLength())
173 self.GotoPos(self.GetLength())
170 if refresh:
174 if refresh:
171 # Maybe this is faster than wx.Yield()
175 current_time = time.time()
172 self.ProcessEvent(wx.PaintEvent())
176 if current_time - self._last_refresh_time > 0.03:
173 #wx.Yield()
177 if sys.platform == 'win32':
178 wx.SafeYield()
179 else:
180 wx.Yield()
181 # self.ProcessEvent(wx.PaintEvent())
182 self._last_refresh_time = current_time
174
183
175
184
176 def new_prompt(self, prompt):
185 def new_prompt(self, prompt):
@@ -183,7 +192,6 b' class ConsoleWidget(editwindow.EditWindow):'
183 # now we update our cursor giving end of prompt
192 # now we update our cursor giving end of prompt
184 self.current_prompt_pos = self.GetLength()
193 self.current_prompt_pos = self.GetLength()
185 self.current_prompt_line = self.GetCurrentLine()
194 self.current_prompt_line = self.GetCurrentLine()
186 wx.Yield()
187 self.EnsureCaretVisible()
195 self.EnsureCaretVisible()
188
196
189
197
@@ -128,6 +128,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
128 # while it is being swapped
128 # while it is being swapped
129 _out_buffer_lock = Lock()
129 _out_buffer_lock = Lock()
130
130
131 # The different line markers used to higlight the prompts.
131 _markers = dict()
132 _markers = dict()
132
133
133 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
@@ -135,12 +136,16 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
135 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
136
137
137 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
139 *args, **kwds):
141 *args, **kwds):
140 """ Create Shell instance.
142 """ Create Shell instance.
141 """
143 """
142 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
143 PrefilterFrontEnd.__init__(self, **kwds)
145 PrefilterFrontEnd.__init__(self, **kwds)
146
147 # Stick in our own raw_input:
148 self.ipython0.raw_input = self.raw_input
144
149
145 # Marker for complete buffer.
150 # Marker for complete buffer.
146 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
@@ -164,9 +169,11 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
164 # Inject self in namespace, for debug
169 # Inject self in namespace, for debug
165 if self.debug:
170 if self.debug:
166 self.shell.user_ns['self'] = self
171 self.shell.user_ns['self'] = self
172 # Inject our own raw_input in namespace
173 self.shell.user_ns['raw_input'] = self.raw_input
167
174
168
175
169 def raw_input(self, prompt):
176 def raw_input(self, prompt=''):
170 """ A replacement from python's raw_input.
177 """ A replacement from python's raw_input.
171 """
178 """
172 self.new_prompt(prompt)
179 self.new_prompt(prompt)
@@ -174,15 +181,13 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
174 if hasattr(self, '_cursor'):
181 if hasattr(self, '_cursor'):
175 del self._cursor
182 del self._cursor
176 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
183 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
177 self.waiting = True
178 self.__old_on_enter = self._on_enter
184 self.__old_on_enter = self._on_enter
185 event_loop = wx.EventLoop()
179 def my_on_enter():
186 def my_on_enter():
180 self.waiting = False
187 event_loop.Exit()
181 self._on_enter = my_on_enter
188 self._on_enter = my_on_enter
182 # XXX: Busy waiting, ugly.
189 # XXX: Running a separate event_loop. Ugly.
183 while self.waiting:
190 event_loop.Run()
184 wx.Yield()
185 sleep(0.1)
186 self._on_enter = self.__old_on_enter
191 self._on_enter = self.__old_on_enter
187 self._input_state = 'buffering'
192 self._input_state = 'buffering'
188 self._cursor = wx.BusyCursor()
193 self._cursor = wx.BusyCursor()
@@ -191,16 +196,18 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
191
196
192 def system_call(self, command_string):
197 def system_call(self, command_string):
193 self._input_state = 'subprocess'
198 self._input_state = 'subprocess'
199 event_loop = wx.EventLoop()
200 def _end_system_call():
201 self._input_state = 'buffering'
202 self._running_process = False
203 event_loop.Exit()
204
194 self._running_process = PipedProcess(command_string,
205 self._running_process = PipedProcess(command_string,
195 out_callback=self.buffered_write,
206 out_callback=self.buffered_write,
196 end_callback = self._end_system_call)
207 end_callback = _end_system_call)
197 self._running_process.start()
208 self._running_process.start()
198 # XXX: another one of these polling loops to have a blocking
209 # XXX: Running a separate event_loop. Ugly.
199 # call
210 event_loop.Run()
200 wx.Yield()
201 while self._running_process:
202 wx.Yield()
203 sleep(0.1)
204 # Be sure to flush the buffer.
211 # Be sure to flush the buffer.
205 self._buffer_flush(event=None)
212 self._buffer_flush(event=None)
206
213
@@ -226,8 +233,9 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
226 for name in symbol_string.split('.')[1:] + ['__doc__']:
233 for name in symbol_string.split('.')[1:] + ['__doc__']:
227 symbol = getattr(symbol, name)
234 symbol = getattr(symbol, name)
228 self.AutoCompCancel()
235 self.AutoCompCancel()
229 wx.Yield()
236 # Check that the symbol can indeed be converted to a string:
230 self.CallTipShow(self.GetCurrentPos(), symbol)
237 symbol += ''
238 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
231 except:
239 except:
232 # The retrieve symbol couldn't be converted to a string
240 # The retrieve symbol couldn't be converted to a string
233 pass
241 pass
@@ -238,9 +246,9 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
238 true, open the menu.
246 true, open the menu.
239 """
247 """
240 if self.debug:
248 if self.debug:
241 print >>sys.__stdout__, "_popup_completion",
249 print >>sys.__stdout__, "_popup_completion"
242 line = self.input_buffer
250 line = self.input_buffer
243 if (self.AutoCompActive() and not line[-1] == '.') \
251 if (self.AutoCompActive() and line and not line[-1] == '.') \
244 or create==True:
252 or create==True:
245 suggestion, completions = self.complete(line)
253 suggestion, completions = self.complete(line)
246 offset=0
254 offset=0
@@ -284,19 +292,21 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
284 if i in self._markers:
292 if i in self._markers:
285 self.MarkerDeleteHandle(self._markers[i])
293 self.MarkerDeleteHandle(self._markers[i])
286 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
294 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
287 # Update the display:
295 # Use a callafter to update the display robustly under windows
288 wx.Yield()
296 def callback():
289 self.GotoPos(self.GetLength())
297 self.GotoPos(self.GetLength())
290 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
298 PrefilterFrontEnd.execute(self, python_string,
299 raw_string=raw_string)
300 wx.CallAfter(callback)
291
301
292 def save_output_hooks(self):
302 def save_output_hooks(self):
293 self.__old_raw_input = __builtin__.raw_input
303 self.__old_raw_input = __builtin__.raw_input
294 PrefilterFrontEnd.save_output_hooks(self)
304 PrefilterFrontEnd.save_output_hooks(self)
295
305
296 def capture_output(self):
306 def capture_output(self):
297 __builtin__.raw_input = self.raw_input
298 self.SetLexer(stc.STC_LEX_NULL)
307 self.SetLexer(stc.STC_LEX_NULL)
299 PrefilterFrontEnd.capture_output(self)
308 PrefilterFrontEnd.capture_output(self)
309 __builtin__.raw_input = self.raw_input
300
310
301
311
302 def release_output(self):
312 def release_output(self):
@@ -316,12 +326,24 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
316 def show_traceback(self):
326 def show_traceback(self):
317 start_line = self.GetCurrentLine()
327 start_line = self.GetCurrentLine()
318 PrefilterFrontEnd.show_traceback(self)
328 PrefilterFrontEnd.show_traceback(self)
319 wx.Yield()
329 self.ProcessEvent(wx.PaintEvent())
330 #wx.Yield()
320 for i in range(start_line, self.GetCurrentLine()):
331 for i in range(start_line, self.GetCurrentLine()):
321 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
332 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
322
333
323
334
324 #--------------------------------------------------------------------------
335 #--------------------------------------------------------------------------
336 # FrontEndBase interface
337 #--------------------------------------------------------------------------
338
339 def render_error(self, e):
340 start_line = self.GetCurrentLine()
341 self.write('\n' + e + '\n')
342 for i in range(start_line, self.GetCurrentLine()):
343 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
344
345
346 #--------------------------------------------------------------------------
325 # ConsoleWidget interface
347 # ConsoleWidget interface
326 #--------------------------------------------------------------------------
348 #--------------------------------------------------------------------------
327
349
@@ -351,7 +373,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
351 if self._input_state == 'subprocess':
373 if self._input_state == 'subprocess':
352 if self.debug:
374 if self.debug:
353 print >>sys.__stderr__, 'Killing running process'
375 print >>sys.__stderr__, 'Killing running process'
354 self._running_process.process.kill()
376 if hasattr(self._running_process, 'process'):
377 self._running_process.process.kill()
355 elif self._input_state == 'buffering':
378 elif self._input_state == 'buffering':
356 if self.debug:
379 if self.debug:
357 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
380 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
@@ -376,7 +399,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
376 char = '\04'
399 char = '\04'
377 self._running_process.process.stdin.write(char)
400 self._running_process.process.stdin.write(char)
378 self._running_process.process.stdin.flush()
401 self._running_process.process.stdin.flush()
379 elif event.KeyCode in (ord('('), 57):
402 elif event.KeyCode in (ord('('), 57, 53):
380 # Calltips
403 # Calltips
381 event.Skip()
404 event.Skip()
382 self.do_calltip()
405 self.do_calltip()
@@ -410,8 +433,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
410 self.input_buffer = new_buffer
433 self.input_buffer = new_buffer
411 # Tab-completion
434 # Tab-completion
412 elif event.KeyCode == ord('\t'):
435 elif event.KeyCode == ord('\t'):
413 last_line = self.input_buffer.split('\n')[-1]
436 current_line, current_line_number = self.CurLine
414 if not re.match(r'^\s*$', last_line):
437 if not re.match(r'^\s*$', current_line):
415 self.complete_current_input()
438 self.complete_current_input()
416 if self.AutoCompActive():
439 if self.AutoCompActive():
417 wx.CallAfter(self._popup_completion, create=True)
440 wx.CallAfter(self._popup_completion, create=True)
@@ -427,7 +450,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
427 if event.KeyCode in (59, ord('.')):
450 if event.KeyCode in (59, ord('.')):
428 # Intercepting '.'
451 # Intercepting '.'
429 event.Skip()
452 event.Skip()
430 self._popup_completion(create=True)
453 wx.CallAfter(self._popup_completion, create=True)
431 else:
454 else:
432 ConsoleWidget._on_key_up(self, event, skip=skip)
455 ConsoleWidget._on_key_up(self, event, skip=skip)
433
456
@@ -456,13 +479,6 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
456 # Private API
479 # Private API
457 #--------------------------------------------------------------------------
480 #--------------------------------------------------------------------------
458
481
459 def _end_system_call(self):
460 """ Called at the end of a system call.
461 """
462 self._input_state = 'buffering'
463 self._running_process = False
464
465
466 def _buffer_flush(self, event):
482 def _buffer_flush(self, event):
467 """ Called by the timer to flush the write buffer.
483 """ Called by the timer to flush the write buffer.
468
484
@@ -16,13 +16,6 b' __docformat__ = "restructuredtext en"'
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
20 # Imports
21 #-------------------------------------------------------------------------------
22 import string
23 import uuid
24 import _ast
25
26 try:
19 try:
27 from zope.interface import Interface, Attribute, implements, classProvides
20 from zope.interface import Interface, Attribute, implements, classProvides
28 except ImportError:
21 except ImportError:
@@ -979,6 +979,38 b' def get_home_dir():'
979 else:
979 else:
980 raise HomeDirError,'support for your operating system not implemented.'
980 raise HomeDirError,'support for your operating system not implemented.'
981
981
982
983 def get_ipython_dir():
984 """Get the IPython directory for this platform and user.
985
986 This uses the logic in `get_home_dir` to find the home directory
987 and the adds either .ipython or _ipython to the end of the path.
988 """
989 if os.name == 'posix':
990 ipdir_def = '.ipython'
991 else:
992 ipdir_def = '_ipython'
993 home_dir = get_home_dir()
994 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
995 os.path.join(home_dir,ipdir_def)))
996 return ipdir
997
998 def get_security_dir():
999 """Get the IPython security directory.
1000
1001 This directory is the default location for all security related files,
1002 including SSL/TLS certificates and FURL files.
1003
1004 If the directory does not exist, it is created with 0700 permissions.
1005 If it exists, permissions are set to 0700.
1006 """
1007 security_dir = os.path.join(get_ipython_dir(), 'security')
1008 if not os.path.isdir(security_dir):
1009 os.mkdir(security_dir, 0700)
1010 else:
1011 os.chmod(security_dir, 0700)
1012 return security_dir
1013
982 #****************************************************************************
1014 #****************************************************************************
983 # strings and text
1015 # strings and text
984
1016
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -8,6 +8,7 b' import os'
8
8
9 # IPython imports
9 # IPython imports
10 from IPython.genutils import Term, ask_yes_no
10 from IPython.genutils import Term, ask_yes_no
11 import IPython.ipapi
11
12
12 def magic_history(self, parameter_s = ''):
13 def magic_history(self, parameter_s = ''):
13 """Print input history (_i<n> variables), with most recent last.
14 """Print input history (_i<n> variables), with most recent last.
@@ -222,6 +223,7 b' class ShadowHist:'
222 # cmd => idx mapping
223 # cmd => idx mapping
223 self.curidx = 0
224 self.curidx = 0
224 self.db = db
225 self.db = db
226 self.disabled = False
225
227
226 def inc_idx(self):
228 def inc_idx(self):
227 idx = self.db.get('shadowhist_idx', 1)
229 idx = self.db.get('shadowhist_idx', 1)
@@ -229,12 +231,19 b' class ShadowHist:'
229 return idx
231 return idx
230
232
231 def add(self, ent):
233 def add(self, ent):
232 old = self.db.hget('shadowhist', ent, _sentinel)
234 if self.disabled:
233 if old is not _sentinel:
234 return
235 return
235 newidx = self.inc_idx()
236 try:
236 #print "new",newidx # dbg
237 old = self.db.hget('shadowhist', ent, _sentinel)
237 self.db.hset('shadowhist',ent, newidx)
238 if old is not _sentinel:
239 return
240 newidx = self.inc_idx()
241 #print "new",newidx # dbg
242 self.db.hset('shadowhist',ent, newidx)
243 except:
244 IPython.ipapi.get().IP.showtraceback()
245 print "WARNING: disabling shadow history"
246 self.disabled = True
238
247
239 def all(self):
248 def all(self):
240 d = self.db.hdict('shadowhist')
249 d = self.db.hdict('shadowhist')
@@ -25,7 +25,8 b' ip = IPython.ipapi.get()'
25 def calljed(self,filename, linenum):
25 def calljed(self,filename, linenum):
26 "My editor hook calls the jed editor directly."
26 "My editor hook calls the jed editor directly."
27 print "Calling my own editor, jed ..."
27 print "Calling my own editor, jed ..."
28 os.system('jed +%d %s' % (linenum,filename))
28 if os.system('jed +%d %s' % (linenum,filename)) != 0:
29 raise ipapi.TryNext()
29
30
30 ip.set_hook('editor', calljed)
31 ip.set_hook('editor', calljed)
31
32
@@ -84,7 +85,8 b' def editor(self,filename, linenum=None):'
84 editor = '"%s"' % editor
85 editor = '"%s"' % editor
85
86
86 # Call the actual editor
87 # Call the actual editor
87 os.system('%s %s %s' % (editor,linemark,filename))
88 if os.system('%s %s %s' % (editor,linemark,filename)) != 0:
89 raise ipapi.TryNext()
88
90
89 import tempfile
91 import tempfile
90 def fix_error_editor(self,filename,linenum,column,msg):
92 def fix_error_editor(self,filename,linenum,column,msg):
@@ -105,7 +107,8 b' def fix_error_editor(self,filename,linenum,column,msg):'
105 return
107 return
106 t = vim_quickfix_file()
108 t = vim_quickfix_file()
107 try:
109 try:
108 os.system('vim --cmd "set errorformat=%f:%l:%c:%m" -q ' + t.name)
110 if os.system('vim --cmd "set errorformat=%f:%l:%c:%m" -q ' + t.name):
111 raise ipapi.TryNext()
109 finally:
112 finally:
110 t.close()
113 t.close()
111
114
@@ -1419,8 +1419,12 b' want to merge them back into the new files.""" % locals()'
1419 except TypeError:
1419 except TypeError:
1420 return 0
1420 return 0
1421 # always pass integer line and offset values to editor hook
1421 # always pass integer line and offset values to editor hook
1422 self.hooks.fix_error_editor(e.filename,
1422 try:
1423 int0(e.lineno),int0(e.offset),e.msg)
1423 self.hooks.fix_error_editor(e.filename,
1424 int0(e.lineno),int0(e.offset),e.msg)
1425 except IPython.ipapi.TryNext:
1426 warn('Could not open editor')
1427 return False
1424 return True
1428 return True
1425
1429
1426 def edit_syntax_error(self):
1430 def edit_syntax_error(self):
@@ -1572,6 +1576,11 b' want to merge them back into the new files.""" % locals()'
1572 else:
1576 else:
1573 banner = self.BANNER+self.banner2
1577 banner = self.BANNER+self.banner2
1574
1578
1579 # if you run stuff with -c <cmd>, raw hist is not updated
1580 # ensure that it's in sync
1581 if len(self.input_hist) != len (self.input_hist_raw):
1582 self.input_hist_raw = InputList(self.input_hist)
1583
1575 while 1:
1584 while 1:
1576 try:
1585 try:
1577 self.interact(banner)
1586 self.interact(banner)
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -15,17 +15,15 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 from os.path import join as pjoin
19
18 from IPython.external.configobj import ConfigObj
20 from IPython.external.configobj import ConfigObj
19 from IPython.config.api import ConfigObjManager
21 from IPython.config.api import ConfigObjManager
20 from IPython.config.cutils import get_ipython_dir
22 from IPython.genutils import get_ipython_dir, get_security_dir
21
23
22 default_kernel_config = ConfigObj()
24 default_kernel_config = ConfigObj()
23
25
24 try:
26 security_dir = get_security_dir()
25 ipython_dir = get_ipython_dir() + '/'
26 except:
27 # This will defaults to the cwd
28 ipython_dir = ''
29
27
30 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
31 # Engine Configuration
29 # Engine Configuration
@@ -33,7 +31,7 b' except:'
33
31
34 engine_config = dict(
32 engine_config = dict(
35 logfile = '', # Empty means log to stdout
33 logfile = '', # Empty means log to stdout
36 furl_file = ipython_dir + 'ipcontroller-engine.furl'
34 furl_file = pjoin(security_dir, 'ipcontroller-engine.furl')
37 )
35 )
38
36
39 #-------------------------------------------------------------------------------
37 #-------------------------------------------------------------------------------
@@ -63,16 +61,17 b' controller_config = dict('
63
61
64 logfile = '', # Empty means log to stdout
62 logfile = '', # Empty means log to stdout
65 import_statement = '',
63 import_statement = '',
64 reuse_furls = False, # If False, old furl files are deleted
66
65
67 engine_tub = dict(
66 engine_tub = dict(
68 ip = '', # Empty string means all interfaces
67 ip = '', # Empty string means all interfaces
69 port = 0, # 0 means pick a port for me
68 port = 0, # 0 means pick a port for me
70 location = '', # Empty string means try to set automatically
69 location = '', # Empty string means try to set automatically
71 secure = True,
70 secure = True,
72 cert_file = ipython_dir + 'ipcontroller-engine.pem',
71 cert_file = pjoin(security_dir, 'ipcontroller-engine.pem'),
73 ),
72 ),
74 engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase',
73 engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase',
75 engine_furl_file = ipython_dir + 'ipcontroller-engine.furl',
74 engine_furl_file = pjoin(security_dir, 'ipcontroller-engine.furl'),
76
75
77 controller_interfaces = dict(
76 controller_interfaces = dict(
78 # multiengine = dict(
77 # multiengine = dict(
@@ -83,12 +82,12 b' controller_config = dict('
83 task = dict(
82 task = dict(
84 controller_interface = 'IPython.kernel.task.ITaskController',
83 controller_interface = 'IPython.kernel.task.ITaskController',
85 fc_interface = 'IPython.kernel.taskfc.IFCTaskController',
84 fc_interface = 'IPython.kernel.taskfc.IFCTaskController',
86 furl_file = ipython_dir + 'ipcontroller-tc.furl'
85 furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
87 ),
86 ),
88 multiengine = dict(
87 multiengine = dict(
89 controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
88 controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
90 fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
89 fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
91 furl_file = ipython_dir + 'ipcontroller-mec.furl'
90 furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
92 )
91 )
93 ),
92 ),
94
93
@@ -97,7 +96,7 b' controller_config = dict('
97 port = 0, # 0 means pick a port for me
96 port = 0, # 0 means pick a port for me
98 location = '', # Empty string means try to set automatically
97 location = '', # Empty string means try to set automatically
99 secure = True,
98 secure = True,
100 cert_file = ipython_dir + 'ipcontroller-client.pem'
99 cert_file = pjoin(security_dir, 'ipcontroller-client.pem')
101 )
100 )
102 )
101 )
103
102
@@ -108,10 +107,10 b' controller_config = dict('
108 client_config = dict(
107 client_config = dict(
109 client_interfaces = dict(
108 client_interfaces = dict(
110 task = dict(
109 task = dict(
111 furl_file = ipython_dir + 'ipcontroller-tc.furl'
110 furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
112 ),
111 ),
113 multiengine = dict(
112 multiengine = dict(
114 furl_file = ipython_dir + 'ipcontroller-mec.furl'
113 furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
115 )
114 )
116 )
115 )
117 )
116 )
@@ -8,8 +8,6 b' which can also be useful as templates for writing new, application-specific'
8 managers.
8 managers.
9 """
9 """
10
10
11 from __future__ import with_statement
12
13 __docformat__ = "restructuredtext en"
11 __docformat__ = "restructuredtext en"
14
12
15 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
@@ -50,7 +50,7 b' from IPython.kernel.engineservice import \\'
50 IEngineSerialized, \
50 IEngineSerialized, \
51 IEngineQueued
51 IEngineQueued
52
52
53 from IPython.config import cutils
53 from IPython.genutils import get_ipython_dir
54 from IPython.kernel import codeutil
54 from IPython.kernel import codeutil
55
55
56 #-------------------------------------------------------------------------------
56 #-------------------------------------------------------------------------------
@@ -170,7 +170,7 b' class ControllerService(object, service.Service):'
170
170
171 def _getEngineInfoLogFile(self):
171 def _getEngineInfoLogFile(self):
172 # Store all logs inside the ipython directory
172 # Store all logs inside the ipython directory
173 ipdir = cutils.get_ipython_dir()
173 ipdir = get_ipython_dir()
174 pjoin = os.path.join
174 pjoin = os.path.join
175 logdir_base = pjoin(ipdir,'log')
175 logdir_base = pjoin(ipdir,'log')
176 if not os.path.isdir(logdir_base):
176 if not os.path.isdir(logdir_base):
@@ -680,6 +680,13 b' class Interpreter(object):'
680 # how trailing whitespace is handled, but this seems to work.
680 # how trailing whitespace is handled, but this seems to work.
681 python = python.strip()
681 python = python.strip()
682
682
683 # The compiler module does not like unicode. We need to convert
684 # it encode it:
685 if isinstance(python, unicode):
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 # encode string.
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
683 # The compiler module will parse the code into an abstract syntax tree.
690 # The compiler module will parse the code into an abstract syntax tree.
684 ast = compiler.parse(python)
691 ast = compiler.parse(python)
685
692
@@ -13,10 +13,17 b' __docformat__ = "restructuredtext en"'
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15
15
16 # Stdlib imports
16 import os
17 import os
17 from cStringIO import StringIO
18 from cStringIO import StringIO
18
19
20 # Our own imports
21 from IPython.testing import decorators as dec
19
22
23 #-----------------------------------------------------------------------------
24 # Test functions
25
26 @dec.skip_win32
20 def test_redirector():
27 def test_redirector():
21 """ Checks that the redirector can be used to do synchronous capture.
28 """ Checks that the redirector can be used to do synchronous capture.
22 """
29 """
@@ -33,9 +40,12 b' def test_redirector():'
33 r.stop()
40 r.stop()
34 raise
41 raise
35 r.stop()
42 r.stop()
36 assert out.getvalue() == "".join("%ic\n%i\n" %(i, i) for i in range(10))
43 result1 = out.getvalue()
44 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
45 assert result1 == result2
37
46
38
47
48 @dec.skip_win32
39 def test_redirector_output_trap():
49 def test_redirector_output_trap():
40 """ This test check not only that the redirector_output_trap does
50 """ This test check not only that the redirector_output_trap does
41 trap the output, but also that it does it in a gready way, that
51 trap the output, but also that it does it in a gready way, that
@@ -54,8 +64,7 b' def test_redirector_output_trap():'
54 trap.unset()
64 trap.unset()
55 raise
65 raise
56 trap.unset()
66 trap.unset()
57 assert out.getvalue() == "".join("%ic\n%ip\n%i\n" %(i, i, i)
67 result1 = out.getvalue()
58 for i in range(10))
68 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
59
69 assert result1 == result2
60
70
61
@@ -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, (730 lines changed) Show them Hide them
@@ -1,324 +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.config import cutils
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
127
103
128 newopt("-l", "--logfile", type="string", dest="logfile",
104 @property
129 help="log file name")
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)
156
157 # def __del__(self):
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')
130
163
131 newopt('-f','--cluster-file',dest='clusterfile',
132 help='file describing a remote cluster')
133
164
134 return parser.parse_args()
165 #-----------------------------------------------------------------------------
166 # Code for launching controller and engines
167 #-----------------------------------------------------------------------------
135
168
136 def numAlive(controller,engines):
137 """Return the number of processes still alive."""
138 retcodes = [controller.poll()] + \
139 [e.poll() for e in engines]
140 return retcodes.count(None)
141
169
142 stop = lambda pid: os.kill(pid,signal.SIGINT)
170 class ControllerLauncher(ProcessLauncher):
143 kill = lambda pid: os.kill(pid,signal.SIGTERM)
171
172 def __init__(self, extra_args=None):
173 if sys.platform == 'win32':
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)
182
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 = cutils.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 # Load the remote cluster configuration
260 clConfig = {}
261 execfile(opt.clusterfile,clConfig)
262 contConfig = clConfig['controller']
263 engConfig = clConfig['engines']
264 # Determine where to find sshx:
265 sshx = clConfig.get('sshx',os.environ.get('IPYTHON_SSHX','sshx'))
266
218
267 # Store all logs inside the ipython directory
219 def _handle_stop(self, r):
268 ipdir = cutils.get_ipython_dir()
220 log.msg('Engines received signal: %r' % r)
269 pjoin = os.path.join
221 return r
270
271 logfile = opt.logfile
272 if logfile is None:
273 logdir_base = pjoin(ipdir,'log')
274 ensureDir(logdir_base)
275 logfile = pjoin(logdir_base,'ipcluster')
276
277 # Append this script's PID to the logfile name always
278 logfile = '%s-%s' % (logfile,os.getpid())
279
222
280 print 'Starting controller:'
223 def signal(self, sig):
281 # Controller data:
224 dlist = []
282 xsys = os.system
225 for el in self.launchers:
283
226 d = el.get_stop_deferred()
284 contHost = contConfig['host']
227 dlist.append(d)
285 contLog = '%s-con-%s-' % (logfile,contHost)
228 el.signal(sig)
286 cmd = "ssh %s '%s' 'ipcontroller --logfile %s' &" % \
229 dfinal = gatherBoth(dlist, consumeErrors=True)
287 (contHost,sshx,contLog)
230 dfinal.addCallback(self._handle_stop)
288 #print 'cmd:<%s>' % cmd # dbg
231 return dfinal
289 xsys(cmd)
232
290 time.sleep(2)
233 def interrupt_then_kill(self, delay=1.0):
291
234 dlist = []
292 print 'Starting engines: '
235 for el in self.launchers:
293 for engineHost,engineData in engConfig.iteritems():
236 d = el.get_stop_deferred()
294 if isinstance(engineData,int):
237 dlist.append(d)
295 numEngines = engineData
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):
245
246 # Subclasses must fill these in. See PBSEngineSet
247 submit_command = ''
248 delete_command = ''
249 job_id_regexp = ''
250
251 def __init__(self, template_file, **kwargs):
252 self.template_file = template_file
253 self.context = {}
254 self.context.update(kwargs)
255 self.batch_file = self.template_file+'-run'
256
257 def parse_job_id(self, output):
258 m = re.match(self.job_id_regexp, output)
259 if m is not None:
260 job_id = m.group()
296 else:
261 else:
297 raise NotImplementedError('port configuration not finished for engines')
262 raise Exception("job id couldn't be determined: %s" % output)
298
263 self.job_id = job_id
299 print 'Sarting %d engines on %s' % (numEngines,engineHost)
264 log.msg('Job started with job id: %r' % job_id)
300 engLog = '%s-eng-%s-' % (logfile,engineHost)
265 return job_id
301 for i in range(numEngines):
266
302 cmd = "ssh %s '%s' 'ipengine --controller-ip %s --logfile %s' &" % \
267 def write_batch_script(self, n):
303 (engineHost,sshx,contHost,engLog)
268 self.context['n'] = n
304 #print 'cmd:<%s>' % cmd # dbg
269 template = open(self.template_file, 'r').read()
305 xsys(cmd)
270 log.msg('Using template for batch script: %s' % self.template_file)
306 # Wait after each host a little bit
271 script_as_string = Itpl.itplns(template, self.context)
307 time.sleep(1)
272 log.msg('Writing instantiated batch script: %s' % self.batch_file)
308
273 f = open(self.batch_file,'w')
309 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
310
288
311 def main():
289 def kill(self):
312 """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):
295
296 submit_command = 'qsub'
297 delete_command = 'qdel'
298 job_id_regexp = '\d+'
313
299
314 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
315
478
316 clusterfile = opt.clusterfile
479 def main():
317 if clusterfile:
480 args = get_args()
318 clusterRemote(opt,arg)
481 reactor.callWhenRunning(args.func, args)
319 else:
482 log.startLogging(sys.stdout)
320 clusterLocal(opt,arg)
483 reactor.run()
321
484
322
485 if __name__ == '__main__':
323 if __name__=='__main__':
324 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
@@ -91,7 +91,7 b' def start_engine():'
91 try:
91 try:
92 engine_service.execute(shell_import_statement)
92 engine_service.execute(shell_import_statement)
93 except:
93 except:
94 log.msg("Error running import_statement: %s" % sis)
94 log.msg("Error running import_statement: %s" % shell_import_statement)
95
95
96 # Create the service hierarchy
96 # Create the service hierarchy
97 main_service = service.MultiService()
97 main_service = service.MultiService()
@@ -105,8 +105,13 b' def start_engine():'
105 # register_engine to tell the controller we are ready to do work
105 # register_engine to tell the controller we are ready to do work
106 engine_connector = EngineConnector(tub_service)
106 engine_connector = EngineConnector(tub_service)
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 d = engine_connector.connect_to_controller(engine_service, furl_file)
109 d = engine_connector.connect_to_controller(engine_service, furl_file)
109 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)
110
115
111 reactor.run()
116 reactor.run()
112
117
@@ -168,4 +173,4 b' def main():'
168
173
169
174
170 if __name__ == "__main__":
175 if __name__ == "__main__":
171 main() No newline at end of file
176 main()
@@ -245,7 +245,7 b' class IEngineSerializedTestCase(object):'
245 self.assert_(es.IEngineSerialized.providedBy(self.engine))
245 self.assert_(es.IEngineSerialized.providedBy(self.engine))
246
246
247 def testIEngineSerializedInterfaceMethods(self):
247 def testIEngineSerializedInterfaceMethods(self):
248 """Does self.engine have the methods and attributes in IEngireCore."""
248 """Does self.engine have the methods and attributes in IEngineCore."""
249 for m in list(es.IEngineSerialized):
249 for m in list(es.IEngineSerialized):
250 self.assert_(hasattr(self.engine, m))
250 self.assert_(hasattr(self.engine, m))
251
251
@@ -288,7 +288,7 b' class IEngineQueuedTestCase(object):'
288 self.assert_(es.IEngineQueued.providedBy(self.engine))
288 self.assert_(es.IEngineQueued.providedBy(self.engine))
289
289
290 def testIEngineQueuedInterfaceMethods(self):
290 def testIEngineQueuedInterfaceMethods(self):
291 """Does self.engine have the methods and attributes in IEngireQueued."""
291 """Does self.engine have the methods and attributes in IEngineQueued."""
292 for m in list(es.IEngineQueued):
292 for m in list(es.IEngineQueued):
293 self.assert_(hasattr(self.engine, m))
293 self.assert_(hasattr(self.engine, m))
294
294
@@ -326,7 +326,7 b' class IEnginePropertiesTestCase(object):'
326 self.assert_(es.IEngineProperties.providedBy(self.engine))
326 self.assert_(es.IEngineProperties.providedBy(self.engine))
327
327
328 def testIEnginePropertiesInterfaceMethods(self):
328 def testIEnginePropertiesInterfaceMethods(self):
329 """Does self.engine have the methods and attributes in IEngireProperties."""
329 """Does self.engine have the methods and attributes in IEngineProperties."""
330 for m in list(es.IEngineProperties):
330 for m in list(es.IEngineProperties):
331 self.assert_(hasattr(self.engine, m))
331 self.assert_(hasattr(self.engine, m))
332
332
@@ -20,7 +20,6 b' from twisted.internet import defer'
20 from IPython.kernel import engineservice as es
20 from IPython.kernel import engineservice as es
21 from IPython.kernel import multiengine as me
21 from IPython.kernel import multiengine as me
22 from IPython.kernel import newserialized
22 from IPython.kernel import newserialized
23 from IPython.kernel.error import NotDefined
24 from IPython.testing import util
23 from IPython.testing import util
25 from IPython.testing.parametric import parametric, Parametric
24 from IPython.testing.parametric import parametric, Parametric
26 from IPython.kernel import newserialized
25 from IPython.kernel import newserialized
@@ -1,4 +1,6 b''
1 from __future__ import with_statement
1 #from __future__ import with_statement
2
3 # XXX This file is currently disabled to preserve 2.4 compatibility.
2
4
3 #def test_simple():
5 #def test_simple():
4 if 0:
6 if 0:
@@ -25,17 +27,17 b' if 0:'
25
27
26 mec.pushAll()
28 mec.pushAll()
27
29
28 with parallel as pr:
30 ## with parallel as pr:
29 # A comment
31 ## # A comment
30 remote() # this means the code below only runs remotely
32 ## remote() # this means the code below only runs remotely
31 print 'Hello remote world'
33 ## print 'Hello remote world'
32 x = range(10)
34 ## x = range(10)
33 # Comments are OK
35 ## # Comments are OK
34 # Even misindented.
36 ## # Even misindented.
35 y = x+1
37 ## y = x+1
36
38
37
39
38 with pfor('i',sequence) as pr:
40 ## with pfor('i',sequence) as pr:
39 print x[i]
41 ## print x[i]
40
42
41 print pr.x + pr.y
43 print pr.x + pr.y
@@ -30,14 +30,15 b' try:'
30 from controllertest import IControllerCoreTestCase
30 from controllertest import IControllerCoreTestCase
31 from IPython.testing.util import DeferredTestCase
31 from IPython.testing.util import DeferredTestCase
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35 class BasicControllerServiceTest(DeferredTestCase,
35
36 IControllerCoreTestCase):
36 class BasicControllerServiceTest(DeferredTestCase,
37
37 IControllerCoreTestCase):
38 def setUp(self):
38
39 self.controller = ControllerService()
39 def setUp(self):
40 self.controller.startService()
40 self.controller = ControllerService()
41
41 self.controller.startService()
42 def tearDown(self):
42
43 self.controller.stopService()
43 def tearDown(self):
44 self.controller.stopService()
@@ -37,56 +37,57 b' try:'
37 IEngineSerializedTestCase, \
37 IEngineSerializedTestCase, \
38 IEngineQueuedTestCase
38 IEngineQueuedTestCase
39 except ImportError:
39 except ImportError:
40 print "we got an error!!!"
40 import nose
41 raise
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
42 else:
42
43 class EngineFCTest(DeferredTestCase,
43
44 IEngineCoreTestCase,
44 class EngineFCTest(DeferredTestCase,
45 IEngineSerializedTestCase,
45 IEngineCoreTestCase,
46 IEngineQueuedTestCase
46 IEngineSerializedTestCase,
47 ):
47 IEngineQueuedTestCase
48
48 ):
49 zi.implements(IControllerBase)
49
50
50 zi.implements(IControllerBase)
51 def setUp(self):
51
52
52 def setUp(self):
53 # Start a server and append to self.servers
53
54 self.controller_reference = FCRemoteEngineRefFromService(self)
54 # Start a server and append to self.servers
55 self.controller_tub = Tub()
55 self.controller_reference = FCRemoteEngineRefFromService(self)
56 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub = Tub()
57 self.controller_tub.setLocation('127.0.0.1:10105')
57 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
58
58 self.controller_tub.setLocation('127.0.0.1:10105')
59 furl = self.controller_tub.registerReference(self.controller_reference)
59
60 self.controller_tub.startService()
60 furl = self.controller_tub.registerReference(self.controller_reference)
61
61 self.controller_tub.startService()
62 # Start an EngineService and append to services/client
62
63 self.engine_service = es.EngineService()
63 # Start an EngineService and append to services/client
64 self.engine_service.startService()
64 self.engine_service = es.EngineService()
65 self.engine_tub = Tub()
65 self.engine_service.startService()
66 self.engine_tub.startService()
66 self.engine_tub = Tub()
67 engine_connector = EngineConnector(self.engine_tub)
67 self.engine_tub.startService()
68 d = engine_connector.connect_to_controller(self.engine_service, furl)
68 engine_connector = EngineConnector(self.engine_tub)
69 # This deferred doesn't fire until after register_engine has returned and
69 d = engine_connector.connect_to_controller(self.engine_service, furl)
70 # thus, self.engine has been defined and the tets can proceed.
70 # This deferred doesn't fire until after register_engine has returned and
71 return d
71 # thus, self.engine has been defined and the tets can proceed.
72
72 return d
73 def tearDown(self):
73
74 dlist = []
74 def tearDown(self):
75 # Shut down the engine
75 dlist = []
76 d = self.engine_tub.stopService()
76 # Shut down the engine
77 dlist.append(d)
77 d = self.engine_tub.stopService()
78 # Shut down the controller
78 dlist.append(d)
79 d = self.controller_tub.stopService()
79 # Shut down the controller
80 dlist.append(d)
80 d = self.controller_tub.stopService()
81 return defer.DeferredList(dlist)
81 dlist.append(d)
82
82 return defer.DeferredList(dlist)
83 #---------------------------------------------------------------------------
83
84 # Make me look like a basic controller
84 #---------------------------------------------------------------------------
85 #---------------------------------------------------------------------------
85 # Make me look like a basic controller
86
86 #---------------------------------------------------------------------------
87 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
87
88 self.engine = IEngineQueued(IEngineBase(engine_ref))
88 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
89 return {'id':id}
89 self.engine = IEngineQueued(IEngineBase(engine_ref))
90
90 return {'id':id}
91 def unregister_engine(self, id):
91
92 pass No newline at end of file
92 def unregister_engine(self, id):
93 pass No newline at end of file
@@ -35,44 +35,46 b' try:'
35 IEngineQueuedTestCase, \
35 IEngineQueuedTestCase, \
36 IEnginePropertiesTestCase
36 IEnginePropertiesTestCase
37 except ImportError:
37 except ImportError:
38 pass
38 import nose
39 else:
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
40 class BasicEngineServiceTest(DeferredTestCase,
40
41 IEngineCoreTestCase,
41
42 IEngineSerializedTestCase,
42 class BasicEngineServiceTest(DeferredTestCase,
43 IEnginePropertiesTestCase):
43 IEngineCoreTestCase,
44
44 IEngineSerializedTestCase,
45 def setUp(self):
45 IEnginePropertiesTestCase):
46 self.engine = es.EngineService()
46
47 self.engine.startService()
47 def setUp(self):
48
48 self.engine = es.EngineService()
49 def tearDown(self):
49 self.engine.startService()
50 return self.engine.stopService()
50
51
51 def tearDown(self):
52 class ThreadedEngineServiceTest(DeferredTestCase,
52 return self.engine.stopService()
53 IEngineCoreTestCase,
53
54 IEngineSerializedTestCase,
54 class ThreadedEngineServiceTest(DeferredTestCase,
55 IEnginePropertiesTestCase):
55 IEngineCoreTestCase,
56
56 IEngineSerializedTestCase,
57 def setUp(self):
57 IEnginePropertiesTestCase):
58 self.engine = es.ThreadedEngineService()
58
59 self.engine.startService()
59 def setUp(self):
60 self.engine = es.ThreadedEngineService()
61 self.engine.startService()
62
63 def tearDown(self):
64 return self.engine.stopService()
65
66 class QueuedEngineServiceTest(DeferredTestCase,
67 IEngineCoreTestCase,
68 IEngineSerializedTestCase,
69 IEnginePropertiesTestCase,
70 IEngineQueuedTestCase):
71
72 def setUp(self):
73 self.rawEngine = es.EngineService()
74 self.rawEngine.startService()
75 self.engine = es.IEngineQueued(self.rawEngine)
60
76
61 def tearDown(self):
77 def tearDown(self):
62 return self.engine.stopService()
78 return self.rawEngine.stopService()
63
79
64 class QueuedEngineServiceTest(DeferredTestCase,
80
65 IEngineCoreTestCase,
66 IEngineSerializedTestCase,
67 IEnginePropertiesTestCase,
68 IEngineQueuedTestCase):
69
70 def setUp(self):
71 self.rawEngine = es.EngineService()
72 self.rawEngine.startService()
73 self.engine = es.IEngineQueued(self.rawEngine)
74
75 def tearDown(self):
76 return self.rawEngine.stopService()
77
78
@@ -23,32 +23,34 b' try:'
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
24 ISynchronousMultiEngineTestCase)
24 ISynchronousMultiEngineTestCase)
25 except ImportError:
25 except ImportError:
26 pass
26 import nose
27 else:
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
28 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
28
29
30 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31
32 def setUp(self):
33 self.controller = ControllerService()
34 self.controller.startService()
35 self.multiengine = me.IMultiEngine(self.controller)
36 self.engines = []
29
37
30 def setUp(self):
38 def tearDown(self):
31 self.controller = ControllerService()
39 self.controller.stopService()
32 self.controller.startService()
40 for e in self.engines:
33 self.multiengine = me.IMultiEngine(self.controller)
41 e.stopService()
34 self.engines = []
42
35
43
36 def tearDown(self):
44 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
37 self.controller.stopService()
45
38 for e in self.engines:
46 def setUp(self):
39 e.stopService()
47 self.controller = ControllerService()
40
48 self.controller.startService()
41
49 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
42 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
50 self.engines = []
43
51
44 def setUp(self):
52 def tearDown(self):
45 self.controller = ControllerService()
53 self.controller.stopService()
46 self.controller.startService()
54 for e in self.engines:
47 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
55 e.stopService()
48 self.engines = []
49
50 def tearDown(self):
51 self.controller.stopService()
52 for e in self.engines:
53 e.stopService()
54
56
@@ -30,115 +30,115 b' try:'
30 from IPython.kernel.error import CompositeError
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
31 from IPython.kernel.util import printer
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35
35
36 def _raise_it(f):
36 def _raise_it(f):
37 try:
37 try:
38 f.raiseException()
38 f.raiseException()
39 except CompositeError, e:
39 except CompositeError, e:
40 e.raise_exception()
40 e.raise_exception()
41
42
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
44
45 def setUp(self):
41
46
47 self.engines = []
48
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
42
57
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
44
60
45 def setUp(self):
61 self.client_tub = ClientConnector()
46
62 d = self.client_tub.get_multiengine_client(furl)
47 self.engines = []
63 d.addCallback(self.handle_got_client)
48
64 return d
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
57
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
60
61 self.client_tub = ClientConnector()
62 d = self.client_tub.get_multiengine_client(furl)
63 d.addCallback(self.handle_got_client)
64 return d
65
66 def handle_got_client(self, client):
67 self.multiengine = client
68
65
69 def tearDown(self):
66 def handle_got_client(self, client):
70 dlist = []
67 self.multiengine = client
71 # Shut down the multiengine client
68
72 d = self.client_tub.tub.stopService()
69 def tearDown(self):
73 dlist.append(d)
70 dlist = []
74 # Shut down the engines
71 # Shut down the multiengine client
75 for e in self.engines:
72 d = self.client_tub.tub.stopService()
76 e.stopService()
73 dlist.append(d)
77 # Shut down the controller
74 # Shut down the engines
78 d = self.controller_tub.stopService()
75 for e in self.engines:
79 d.addBoth(lambda _: self.controller.stopService())
76 e.stopService()
80 dlist.append(d)
77 # Shut down the controller
81 return defer.DeferredList(dlist)
78 d = self.controller_tub.stopService()
79 d.addBoth(lambda _: self.controller.stopService())
80 dlist.append(d)
81 return defer.DeferredList(dlist)
82
82
83 def test_mapper(self):
83 def test_mapper(self):
84 self.addEngine(4)
84 self.addEngine(4)
85 m = self.multiengine.mapper()
85 m = self.multiengine.mapper()
86 self.assertEquals(m.multiengine,self.multiengine)
86 self.assertEquals(m.multiengine,self.multiengine)
87 self.assertEquals(m.dist,'b')
87 self.assertEquals(m.dist,'b')
88 self.assertEquals(m.targets,'all')
88 self.assertEquals(m.targets,'all')
89 self.assertEquals(m.block,True)
89 self.assertEquals(m.block,True)
90
90
91 def test_map_default(self):
91 def test_map_default(self):
92 self.addEngine(4)
92 self.addEngine(4)
93 m = self.multiengine.mapper()
93 m = self.multiengine.mapper()
94 d = m.map(lambda x: 2*x, range(10))
94 d = m.map(lambda x: 2*x, range(10))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
98 return d
98 return d
99
99
100 def test_map_noblock(self):
100 def test_map_noblock(self):
101 self.addEngine(4)
101 self.addEngine(4)
102 m = self.multiengine.mapper(block=False)
102 m = self.multiengine.mapper(block=False)
103 d = m.map(lambda x: 2*x, range(10))
103 d = m.map(lambda x: 2*x, range(10))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 return d
106 return d
107
107
108 def test_mapper_fail(self):
108 def test_mapper_fail(self):
109 self.addEngine(4)
109 self.addEngine(4)
110 m = self.multiengine.mapper()
110 m = self.multiengine.mapper()
111 d = m.map(lambda x: 1/0, range(10))
111 d = m.map(lambda x: 1/0, range(10))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
113 return d
113 return d
114
114
115 def test_parallel(self):
115 def test_parallel(self):
116 self.addEngine(4)
116 self.addEngine(4)
117 p = self.multiengine.parallel()
117 p = self.multiengine.parallel()
118 self.assert_(isinstance(p, ParallelFunction))
118 self.assert_(isinstance(p, ParallelFunction))
119 @p
119 @p
120 def f(x): return 2*x
120 def f(x): return 2*x
121 d = f(range(10))
121 d = f(range(10))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
123 return d
123 return d
124
124
125 def test_parallel_noblock(self):
125 def test_parallel_noblock(self):
126 self.addEngine(1)
126 self.addEngine(1)
127 p = self.multiengine.parallel(block=False)
127 p = self.multiengine.parallel(block=False)
128 self.assert_(isinstance(p, ParallelFunction))
128 self.assert_(isinstance(p, ParallelFunction))
129 @p
129 @p
130 def f(x): return 2*x
130 def f(x): return 2*x
131 d = f(range(10))
131 d = f(range(10))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
134 return d
134 return d
135
135
136 def test_parallel_fail(self):
136 def test_parallel_fail(self):
137 self.addEngine(4)
137 self.addEngine(4)
138 p = self.multiengine.parallel()
138 p = self.multiengine.parallel()
139 self.assert_(isinstance(p, ParallelFunction))
139 self.assert_(isinstance(p, ParallelFunction))
140 @p
140 @p
141 def f(x): return 1/0
141 def f(x): return 1/0
142 d = f(range(10))
142 d = f(range(10))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
144 return d No newline at end of file
144 return d No newline at end of file
@@ -28,75 +28,75 b' try:'
28 SerializeIt, \
28 SerializeIt, \
29 UnSerializeIt
29 UnSerializeIt
30 except ImportError:
30 except ImportError:
31 pass
31 import nose
32 else:
32 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
33 #-------------------------------------------------------------------------------
33
34 # Tests
34 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
35 # Tests
36 #-------------------------------------------------------------------------------
37
38 class SerializedTestCase(unittest.TestCase):
39
40 def setUp(self):
41 pass
36
42
37 class SerializedTestCase(unittest.TestCase):
43 def tearDown(self):
44 pass
38
45
39 def setUp(self):
46 def testSerializedInterfaces(self):
40 pass
41
42 def tearDown(self):
43 pass
44
45 def testSerializedInterfaces(self):
46
47
47 us = UnSerialized({'a':10, 'b':range(10)})
48 us = UnSerialized({'a':10, 'b':range(10)})
48 s = ISerialized(us)
49 s = ISerialized(us)
49 uss = IUnSerialized(s)
50 uss = IUnSerialized(s)
50 self.assert_(ISerialized.providedBy(s))
51 self.assert_(ISerialized.providedBy(s))
51 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(uss))
53 self.assert_(IUnSerialized.providedBy(uss))
53 for m in list(ISerialized):
54 for m in list(ISerialized):
54 self.assert_(hasattr(s, m))
55 self.assert_(hasattr(s, m))
55 for m in list(IUnSerialized):
56 for m in list(IUnSerialized):
56 self.assert_(hasattr(us, m))
57 self.assert_(hasattr(us, m))
57 for m in list(IUnSerialized):
58 for m in list(IUnSerialized):
58 self.assert_(hasattr(uss, m))
59 self.assert_(hasattr(uss, m))
59
60
60 def testPickleSerialized(self):
61 def testPickleSerialized(self):
61 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 original = UnSerialized(obj)
63 original = UnSerialized(obj)
63 originalSer = ISerialized(original)
64 originalSer = ISerialized(original)
64 firstData = originalSer.getData()
65 firstData = originalSer.getData()
65 firstTD = originalSer.getTypeDescriptor()
66 firstTD = originalSer.getTypeDescriptor()
66 firstMD = originalSer.getMetadata()
67 firstMD = originalSer.getMetadata()
67 self.assert_(firstTD == 'pickle')
68 self.assert_(firstTD == 'pickle')
68 self.assert_(firstMD == {})
69 self.assert_(firstMD == {})
69 unSerialized = IUnSerialized(originalSer)
70 unSerialized = IUnSerialized(originalSer)
70 secondObj = unSerialized.getObject()
71 secondObj = unSerialized.getObject()
71 for k, v in secondObj.iteritems():
72 for k, v in secondObj.iteritems():
72 self.assert_(obj[k] == v)
73 self.assert_(obj[k] == v)
73 secondSer = ISerialized(UnSerialized(secondObj))
74 secondSer = ISerialized(UnSerialized(secondObj))
74 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstMD == secondSer.getMetadata())
77 self.assert_(firstMD == secondSer.getMetadata())
78
79 def testNDArraySerialized(self):
80 try:
81 import numpy
82 except ImportError:
83 pass
84 else:
85 a = numpy.linspace(0.0, 1.0, 1000)
86 unSer1 = UnSerialized(a)
87 ser1 = ISerialized(unSer1)
88 td = ser1.getTypeDescriptor()
89 self.assert_(td == 'ndarray')
90 md = ser1.getMetadata()
91 self.assert_(md['shape'] == a.shape)
92 self.assert_(md['dtype'] == a.dtype.str)
93 buff = ser1.getData()
94 self.assert_(buff == numpy.getbuffer(a))
95 s = Serialized(buff, td, md)
96 us = IUnSerialized(s)
97 final = us.getObject()
98 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
99 self.assert_(a.dtype.str == final.dtype.str)
100 self.assert_(a.shape == final.shape)
101
77
102
78 def testNDArraySerialized(self):
79 try:
80 import numpy
81 except ImportError:
82 pass
83 else:
84 a = numpy.linspace(0.0, 1.0, 1000)
85 unSer1 = UnSerialized(a)
86 ser1 = ISerialized(unSer1)
87 td = ser1.getTypeDescriptor()
88 self.assert_(td == 'ndarray')
89 md = ser1.getMetadata()
90 self.assert_(md['shape'] == a.shape)
91 self.assert_(md['dtype'] == a.dtype.str)
92 buff = ser1.getData()
93 self.assert_(buff == numpy.getbuffer(a))
94 s = Serialized(buff, td, md)
95 us = IUnSerialized(s)
96 final = us.getObject()
97 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
98 self.assert_(a.dtype.str == final.dtype.str)
99 self.assert_(a.shape == final.shape)
100
101
102 No newline at end of file
@@ -25,162 +25,162 b' try:'
25 from IPython.kernel import error
25 from IPython.kernel import error
26 from IPython.kernel.util import printer
26 from IPython.kernel.util import printer
27 except ImportError:
27 except ImportError:
28 pass
28 import nose
29 else:
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
30
31 class Foo(object):
31 class Foo(object):
32
33 def bar(self, bahz):
34 return defer.succeed('blahblah: %s' % bahz)
35
32
36 class TwoPhaseFoo(pd.PendingDeferredManager):
33 def bar(self, bahz):
37
34 return defer.succeed('blahblah: %s' % bahz)
38 def __init__(self, foo):
39 self.foo = foo
40 pd.PendingDeferredManager.__init__(self)
41
35
42 @pd.two_phase
36 class TwoPhaseFoo(pd.PendingDeferredManager):
43 def bar(self, bahz):
44 return self.foo.bar(bahz)
45
37
46 class PendingDeferredManagerTest(DeferredTestCase):
38 def __init__(self, foo):
47
39 self.foo = foo
48 def setUp(self):
40 pd.PendingDeferredManager.__init__(self)
49 self.pdm = pd.PendingDeferredManager()
50
51 def tearDown(self):
52 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
58 d = defer.Deferred()
59 did = self.pdm.save_pending_deferred(d)
60 dDict[did] = d
61 # Make sure they are begin saved
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
41
109 def test_get_before_callback(self):
42 @pd.two_phase
110 d = defer.Deferred()
43 def bar(self, bahz):
111 did = self.pdm.save_pending_deferred(d)
44 return self.foo.bar(bahz)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
148
149 def test_noresult_noblock(self):
150 d = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
152 d2 = self.pdm.get_pending_deferred(did,False)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
154
45
155 def test_with_callbacks(self):
46 class PendingDeferredManagerTest(DeferredTestCase):
156 d = defer.Deferred()
47
157 d.addCallback(lambda r: r+' foo')
48 def setUp(self):
158 d.addCallback(lambda r: r+' bar')
49 self.pdm = pd.PendingDeferredManager()
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
50
164 def test_with_errbacks(self):
51 def tearDown(self):
165 class MyError(Exception):
52 pass
166 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
167 d = defer.Deferred()
58 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
59 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
60 dDict[did] = d
172 d.errback(failure.Failure(MyError('bam')))
61 # Make sure they are begin saved
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
109 def test_get_before_callback(self):
110 d = defer.Deferred()
111 did = self.pdm.save_pending_deferred(d)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
174
148
175 def test_nested_deferreds(self):
149 def test_noresult_noblock(self):
176 d = defer.Deferred()
150 d = defer.Deferred()
177 d2 = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
178 d.addCallback(lambda r: d2)
152 d2 = self.pdm.get_pending_deferred(did,False)
179 did = self.pdm.save_pending_deferred(d)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
180 d.callback('foo')
154
181 d3 = self.pdm.get_pending_deferred(did,False)
155 def test_with_callbacks(self):
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
156 d = defer.Deferred()
183 d2.callback('bar')
157 d.addCallback(lambda r: r+' foo')
184 d3 = self.pdm.get_pending_deferred(did,False)
158 d.addCallback(lambda r: r+' bar')
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
164 def test_with_errbacks(self):
165 class MyError(Exception):
166 pass
167 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
172 d.errback(failure.Failure(MyError('bam')))
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
174
175 def test_nested_deferreds(self):
176 d = defer.Deferred()
177 d2 = defer.Deferred()
178 d.addCallback(lambda r: d2)
179 did = self.pdm.save_pending_deferred(d)
180 d.callback('foo')
181 d3 = self.pdm.get_pending_deferred(did,False)
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
183 d2.callback('bar')
184 d3 = self.pdm.get_pending_deferred(did,False)
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
186
186
@@ -26,25 +26,26 b' try:'
26 from IPython.testing.util import DeferredTestCase
26 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
28 except ImportError:
28 except ImportError:
29 pass
29 import nose
30 else:
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
31 #-------------------------------------------------------------------------------
32 # Tests
33 #-------------------------------------------------------------------------------
34
31
35 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
32 #-------------------------------------------------------------------------------
33 # Tests
34 #-------------------------------------------------------------------------------
35
36 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
37
38 def setUp(self):
39 self.controller = cs.ControllerService()
40 self.controller.startService()
41 self.multiengine = IMultiEngine(self.controller)
42 self.tc = task.ITaskController(self.controller)
43 self.tc.failurePenalty = 0
44 self.engines=[]
36
45
37 def setUp(self):
46 def tearDown(self):
38 self.controller = cs.ControllerService()
47 self.controller.stopService()
39 self.controller.startService()
48 for e in self.engines:
40 self.multiengine = IMultiEngine(self.controller)
49 e.stopService()
41 self.tc = task.ITaskController(self.controller)
42 self.tc.failurePenalty = 0
43 self.engines=[]
44
45 def tearDown(self):
46 self.controller.stopService()
47 for e in self.engines:
48 e.stopService()
49
50
50
51
@@ -33,129 +33,130 b' try:'
33 from IPython.kernel.error import CompositeError
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.parallelfunction import ParallelFunction
35 except ImportError:
35 except ImportError:
36 pass
36 import nose
37 else:
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
38
38
39 #-------------------------------------------------------------------------------
40 # Tests
41 #-------------------------------------------------------------------------------
42
39
43 def _raise_it(f):
40 #-------------------------------------------------------------------------------
44 try:
41 # Tests
45 f.raiseException()
42 #-------------------------------------------------------------------------------
46 except CompositeError, e:
47 e.raise_exception()
48
43
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
44 def _raise_it(f):
45 try:
46 f.raiseException()
47 except CompositeError, e:
48 e.raise_exception()
50
49
51 def setUp(self):
50 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
52
51
53 self.engines = []
52 def setUp(self):
54
53
55 self.controller = cs.ControllerService()
54 self.engines = []
56 self.controller.startService()
55
57 self.imultiengine = me.IMultiEngine(self.controller)
56 self.controller = cs.ControllerService()
58 self.itc = taskmodule.ITaskController(self.controller)
57 self.controller.startService()
59 self.itc.failurePenalty = 0
58 self.imultiengine = me.IMultiEngine(self.controller)
60
59 self.itc = taskmodule.ITaskController(self.controller)
61 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
60 self.itc.failurePenalty = 0
62 self.tc_referenceable = IFCTaskController(self.itc)
61
63
62 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
64 self.controller_tub = Tub()
63 self.tc_referenceable = IFCTaskController(self.itc)
65 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
64
66 self.controller_tub.setLocation('127.0.0.1:10105')
65 self.controller_tub = Tub()
67
66 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
68 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
67 self.controller_tub.setLocation('127.0.0.1:10105')
69 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
68
70 self.controller_tub.startService()
69 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
71
70 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
72 self.client_tub = ClientConnector()
71 self.controller_tub.startService()
73 d = self.client_tub.get_multiengine_client(mec_furl)
72
74 d.addCallback(self.handle_mec_client)
73 self.client_tub = ClientConnector()
75 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
74 d = self.client_tub.get_multiengine_client(mec_furl)
76 d.addCallback(self.handle_tc_client)
75 d.addCallback(self.handle_mec_client)
77 return d
76 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
78
77 d.addCallback(self.handle_tc_client)
79 def handle_mec_client(self, client):
78 return d
80 self.multiengine = client
79
80 def handle_mec_client(self, client):
81 self.multiengine = client
82
83 def handle_tc_client(self, client):
84 self.tc = client
85
86 def tearDown(self):
87 dlist = []
88 # Shut down the multiengine client
89 d = self.client_tub.tub.stopService()
90 dlist.append(d)
91 # Shut down the engines
92 for e in self.engines:
93 e.stopService()
94 # Shut down the controller
95 d = self.controller_tub.stopService()
96 d.addBoth(lambda _: self.controller.stopService())
97 dlist.append(d)
98 return defer.DeferredList(dlist)
99
100 def test_mapper(self):
101 self.addEngine(1)
102 m = self.tc.mapper()
103 self.assertEquals(m.task_controller,self.tc)
104 self.assertEquals(m.clear_before,False)
105 self.assertEquals(m.clear_after,False)
106 self.assertEquals(m.retries,0)
107 self.assertEquals(m.recovery_task,None)
108 self.assertEquals(m.depend,None)
109 self.assertEquals(m.block,True)
110
111 def test_map_default(self):
112 self.addEngine(1)
113 m = self.tc.mapper()
114 d = m.map(lambda x: 2*x, range(10))
115 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
116 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
117 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
118 return d
119
120 def test_map_noblock(self):
121 self.addEngine(1)
122 m = self.tc.mapper(block=False)
123 d = m.map(lambda x: 2*x, range(10))
124 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
125 return d
126
127 def test_mapper_fail(self):
128 self.addEngine(1)
129 m = self.tc.mapper()
130 d = m.map(lambda x: 1/0, range(10))
131 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
132 return d
133
134 def test_parallel(self):
135 self.addEngine(1)
136 p = self.tc.parallel()
137 self.assert_(isinstance(p, ParallelFunction))
138 @p
139 def f(x): return 2*x
140 d = f(range(10))
141 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
142 return d
81
143
82 def handle_tc_client(self, client):
144 def test_parallel_noblock(self):
83 self.tc = client
145 self.addEngine(1)
146 p = self.tc.parallel(block=False)
147 self.assert_(isinstance(p, ParallelFunction))
148 @p
149 def f(x): return 2*x
150 d = f(range(10))
151 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
152 return d
84
153
85 def tearDown(self):
154 def test_parallel_fail(self):
86 dlist = []
155 self.addEngine(1)
87 # Shut down the multiengine client
156 p = self.tc.parallel()
88 d = self.client_tub.tub.stopService()
157 self.assert_(isinstance(p, ParallelFunction))
89 dlist.append(d)
158 @p
90 # Shut down the engines
159 def f(x): return 1/0
91 for e in self.engines:
160 d = f(range(10))
92 e.stopService()
161 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
93 # Shut down the controller
162 return d No newline at end of file
94 d = self.controller_tub.stopService()
95 d.addBoth(lambda _: self.controller.stopService())
96 dlist.append(d)
97 return defer.DeferredList(dlist)
98
99 def test_mapper(self):
100 self.addEngine(1)
101 m = self.tc.mapper()
102 self.assertEquals(m.task_controller,self.tc)
103 self.assertEquals(m.clear_before,False)
104 self.assertEquals(m.clear_after,False)
105 self.assertEquals(m.retries,0)
106 self.assertEquals(m.recovery_task,None)
107 self.assertEquals(m.depend,None)
108 self.assertEquals(m.block,True)
109
110 def test_map_default(self):
111 self.addEngine(1)
112 m = self.tc.mapper()
113 d = m.map(lambda x: 2*x, range(10))
114 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
115 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
116 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
117 return d
118
119 def test_map_noblock(self):
120 self.addEngine(1)
121 m = self.tc.mapper(block=False)
122 d = m.map(lambda x: 2*x, range(10))
123 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
124 return d
125
126 def test_mapper_fail(self):
127 self.addEngine(1)
128 m = self.tc.mapper()
129 d = m.map(lambda x: 1/0, range(10))
130 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
131 return d
132
133 def test_parallel(self):
134 self.addEngine(1)
135 p = self.tc.parallel()
136 self.assert_(isinstance(p, ParallelFunction))
137 @p
138 def f(x): return 2*x
139 d = f(range(10))
140 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
141 return d
142
143 def test_parallel_noblock(self):
144 self.addEngine(1)
145 p = self.tc.parallel(block=False)
146 self.assert_(isinstance(p, ParallelFunction))
147 @p
148 def f(x): return 2*x
149 d = f(range(10))
150 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
151 return d
152
153 def test_parallel_fail(self):
154 self.addEngine(1)
155 p = self.tc.parallel()
156 self.assert_(isinstance(p, ParallelFunction))
157 @p
158 def f(x): return 1/0
159 d = f(range(10))
160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
161 return d No newline at end of file
@@ -8,6 +8,10 b' the decorator, in order to preserve metadata such as function name,'
8 setup and teardown functions and so on - see nose.tools for more
8 setup and teardown functions and so on - see nose.tools for more
9 information.
9 information.
10
10
11 This module provides a set of useful decorators meant to be ready to use in
12 your own tests. See the bottom of the file for the ready-made ones, and if you
13 find yourself writing a new one that may be of generic use, add it here.
14
11 NOTE: This file contains IPython-specific decorators and imports the
15 NOTE: This file contains IPython-specific decorators and imports the
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
16 numpy.testing.decorators file, which we've copied verbatim. Any of our own
13 code will be added at the bottom if we end up extending this.
17 code will be added at the bottom if we end up extending this.
@@ -15,6 +19,7 b' code will be added at the bottom if we end up extending this.'
15
19
16 # Stdlib imports
20 # Stdlib imports
17 import inspect
21 import inspect
22 import sys
18
23
19 # Third-party imports
24 # Third-party imports
20
25
@@ -120,16 +125,36 b" skip_doctest = make_label_dec('skip_doctest',"
120 omit from testing, while preserving the docstring for introspection, help,
125 omit from testing, while preserving the docstring for introspection, help,
121 etc.""")
126 etc.""")
122
127
128 def skip(msg=''):
129 """Decorator - mark a test function for skipping from test suite.
130
131 This function *is* already a decorator, it is not a factory like
132 make_label_dec or some of those in decorators_numpy.
133
134 :Parameters:
135
136 func : function
137 Test function to be skipped
123
138
124 def skip(func):
139 msg : string
125 """Decorator - mark a test function for skipping from test suite."""
140 Optional message to be added.
141 """
126
142
127 import nose
143 import nose
128
129 def wrapper(*a,**k):
130 raise nose.SkipTest("Skipping test for function: %s" %
131 func.__name__)
132
133 return apply_wrapper(wrapper,func)
134
144
145 def inner(func):
146
147 def wrapper(*a,**k):
148 if msg: out = '\n'+msg
149 else: out = ''
150 raise nose.SkipTest("Skipping test for function: %s%s" %
151 (func.__name__,out))
152
153 return apply_wrapper(wrapper,func)
154
155 return inner
135
156
157 # Decorators to skip certain tests on specific platforms.
158 skip_win32 = skipif(sys.platform=='win32',"This test does not run under Windows")
159 skip_linux = skipif(sys.platform=='linux2',"This test does not run under Linux")
160 skip_osx = skipif(sys.platform=='darwin',"This test does not run under OSX")
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
@@ -1,6 +1,5 b''
1 # Set this prefix to where you want to install the plugin
1 # Set this prefix to where you want to install the plugin
2 PREFIX=~/usr/local
2 PREFIX=/usr/local
3 PREFIX=~/tmp/local
4
3
5 NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors
4 NOSE0=nosetests -vs --with-doctest --doctest-tests --detailed-errors
6 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \
5 NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \
@@ -658,6 +658,24 b' class ExtensionDoctest(doctests.Doctest):'
658
658
659 def options(self, parser, env=os.environ):
659 def options(self, parser, env=os.environ):
660 Plugin.options(self, parser, env)
660 Plugin.options(self, parser, env)
661 parser.add_option('--doctest-tests', action='store_true',
662 dest='doctest_tests',
663 default=env.get('NOSE_DOCTEST_TESTS',True),
664 help="Also look for doctests in test modules. "
665 "Note that classes, methods and functions should "
666 "have either doctests or non-doctest tests, "
667 "not both. [NOSE_DOCTEST_TESTS]")
668 parser.add_option('--doctest-extension', action="append",
669 dest="doctestExtension",
670 help="Also look for doctests in files with "
671 "this extension [NOSE_DOCTEST_EXTENSION]")
672 # Set the default as a list, if given in env; otherwise
673 # an additional value set on the command line will cause
674 # an error.
675 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
676 if env_setting is not None:
677 parser.set_defaults(doctestExtension=tolist(env_setting))
678
661
679
662 def configure(self, options, config):
680 def configure(self, options, config):
663 Plugin.configure(self, options, config)
681 Plugin.configure(self, options, config)
@@ -743,16 +761,19 b' class ExtensionDoctest(doctests.Doctest):'
743 Modified version that accepts extension modules as valid containers for
761 Modified version that accepts extension modules as valid containers for
744 doctests.
762 doctests.
745 """
763 """
746 #print 'Filename:',filename # dbg
764 print 'Filename:',filename # dbg
747
765
748 # XXX - temporarily hardcoded list, will move to driver later
766 # XXX - temporarily hardcoded list, will move to driver later
749 exclude = ['IPython/external/',
767 exclude = ['IPython/external/',
750 'IPython/Extensions/ipy_',
751 'IPython/platutils_win32',
768 'IPython/platutils_win32',
752 'IPython/frontend/cocoa',
769 'IPython/frontend/cocoa',
753 'IPython_doctest_plugin',
770 'IPython_doctest_plugin',
754 'IPython/Gnuplot',
771 'IPython/Gnuplot',
755 'IPython/Extensions/PhysicalQIn']
772 'IPython/Extensions/ipy_',
773 'IPython/Extensions/PhysicalQIn',
774 'IPython/Extensions/scitedirector',
775 'IPython/testing/plugin',
776 ]
756
777
757 for fex in exclude:
778 for fex in exclude:
758 if fex in filename: # substring
779 if fex in filename: # substring
@@ -782,3 +803,4 b' class IPythonDoctest(ExtensionDoctest):'
782 self.checker = IPDoctestOutputChecker()
803 self.checker = IPDoctestOutputChecker()
783 self.globs = None
804 self.globs = None
784 self.extraglobs = None
805 self.extraglobs = None
806
@@ -1,147 +1,19 b''
1 """Some simple tests for the plugin while running scripts.
2 """
1 # Module imports
3 # Module imports
2 # Std lib
4 # Std lib
3 import inspect
5 import inspect
4
6
5 # Third party
6
7 # Our own
7 # Our own
8 from IPython.testing import decorators as dec
8 from IPython.testing import decorators as dec
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Utilities
12
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
14 # circular dependencies and is easier to reuse.
15 def getargspec(obj):
16 """Get the names and default values of a function's arguments.
17
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
19 'args' is a list of the argument names (it may contain nested lists).
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
21 'defaults' is an n-tuple of the default values of the last n arguments.
22
23 Modified version of inspect.getargspec from the Python Standard
24 Library."""
25
26 if inspect.isfunction(obj):
27 func_obj = obj
28 elif inspect.ismethod(obj):
29 func_obj = obj.im_func
30 else:
31 raise TypeError, 'arg is not a Python function'
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
33 return args, varargs, varkw, func_obj.func_defaults
34
35 #-----------------------------------------------------------------------------
36 # Testing functions
11 # Testing functions
37
12
38 def test_trivial():
13 def test_trivial():
39 """A trivial passing test."""
14 """A trivial passing test."""
40 pass
15 pass
41
16
42
43 @dec.skip
44 def test_deliberately_broken():
45 """A deliberately broken test - we want to skip this one."""
46 1/0
47
48
49 # Verify that we can correctly skip the doctest for a function at will, but
50 # that the docstring itself is NOT destroyed by the decorator.
51 @dec.skip_doctest
52 def doctest_bad(x,y=1,**k):
53 """A function whose doctest we need to skip.
54
55 >>> 1+1
56 3
57 """
58 print 'x:',x
59 print 'y:',y
60 print 'k:',k
61
62
63 def call_doctest_bad():
64 """Check that we can still call the decorated functions.
65
66 >>> doctest_bad(3,y=4)
67 x: 3
68 y: 4
69 k: {}
70 """
71 pass
72
73
74 # Doctest skipping should work for class methods too
75 class foo(object):
76 """Foo
77
78 Example:
79
80 >>> 1+1
81 2
82 """
83
84 @dec.skip_doctest
85 def __init__(self,x):
86 """Make a foo.
87
88 Example:
89
90 >>> f = foo(3)
91 junk
92 """
93 print 'Making a foo.'
94 self.x = x
95
96 @dec.skip_doctest
97 def bar(self,y):
98 """Example:
99
100 >>> f = foo(3)
101 >>> f.bar(0)
102 boom!
103 >>> 1/0
104 bam!
105 """
106 return 1/y
107
108 def baz(self,y):
109 """Example:
110
111 >>> f = foo(3)
112 Making a foo.
113 >>> f.baz(3)
114 True
115 """
116 return self.x==y
117
118
119 def test_skip_dt_decorator():
120 """Doctest-skipping decorator should preserve the docstring.
121 """
122 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
123 check = """A function whose doctest we need to skip.
124
125 >>> 1+1
126 3
127 """
128 # Fetch the docstring from doctest_bad after decoration.
129 val = doctest_bad.__doc__
130
131 assert check==val,"doctest_bad docstrings don't match"
132
133
134 def test_skip_dt_decorator2():
135 """Doctest-skipping decorator should preserve function signature.
136 """
137 # Hardcoded correct answer
138 dtargs = (['x', 'y'], None, 'k', (1,))
139 # Introspect out the value
140 dtargsr = getargspec(doctest_bad)
141 assert dtargsr==dtargs, \
142 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
143
144
145 def doctest_run():
17 def doctest_run():
146 """Test running a trivial script.
18 """Test running a trivial script.
147
19
@@ -149,7 +21,6 b' def doctest_run():'
149 x is: 1
21 x is: 1
150 """
22 """
151
23
152 #@dec.skip_doctest
153 def doctest_runvars():
24 def doctest_runvars():
154 """Test that variables defined in scripts get loaded correcly via %run.
25 """Test that variables defined in scripts get loaded correcly via %run.
155
26
@@ -9,6 +9,7 b' Utilities for testing code.'
9 # testing machinery from snakeoil that were good have already been merged into
9 # testing machinery from snakeoil that were good have already been merged into
10 # the nose plugin, so this can be taken away soon. Leave a warning for now,
10 # the nose plugin, so this can be taken away soon. Leave a warning for now,
11 # we'll remove it in a later release (around 0.10 or so).
11 # we'll remove it in a later release (around 0.10 or so).
12
12 from warnings import warn
13 from warnings import warn
13 warn('This will be removed soon. Use IPython.testing.util instead',
14 warn('This will be removed soon. Use IPython.testing.util instead',
14 DeprecationWarning)
15 DeprecationWarning)
@@ -445,7 +445,8 b' class ListTB(TBTools):'
445
445
446 Also lifted nearly verbatim from traceback.py
446 Also lifted nearly verbatim from traceback.py
447 """
447 """
448
448
449 have_filedata = False
449 Colors = self.Colors
450 Colors = self.Colors
450 list = []
451 list = []
451 try:
452 try:
@@ -459,8 +460,9 b' class ListTB(TBTools):'
459 try:
460 try:
460 msg, (filename, lineno, offset, line) = value
461 msg, (filename, lineno, offset, line) = value
461 except:
462 except:
462 pass
463 have_filedata = False
463 else:
464 else:
465 have_filedata = True
464 #print 'filename is',filename # dbg
466 #print 'filename is',filename # dbg
465 if not filename: filename = "<string>"
467 if not filename: filename = "<string>"
466 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
468 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
@@ -492,7 +494,8 b' class ListTB(TBTools):'
492 list.append('%s\n' % str(stype))
494 list.append('%s\n' % str(stype))
493
495
494 # vds:>>
496 # vds:>>
495 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
497 if have_filedata:
498 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
496 # vds:<<
499 # vds:<<
497
500
498 return list
501 return list
@@ -809,10 +812,10 b' class VerboseTB(TBTools):'
809
812
810 # vds: >>
813 # vds: >>
811 if records:
814 if records:
812 frame, file, lnum, func, lines, index = records[-1]
815 filepath, lnum = records[-1][1:3]
813 #print "file:", str(file), "linenb", str(lnum)
816 #print "file:", str(file), "linenb", str(lnum) # dbg
814 file = abspath(file)
817 filepath = os.path.abspath(filepath)
815 __IPYTHON__.hooks.synchronize_with_editor(file, lnum, 0)
818 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
816 # vds: <<
819 # vds: <<
817
820
818 # return all our info assembled as a single string
821 # return all our info assembled as a single string
@@ -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
@@ -30,3 +29,4 b' global-exclude *.pyc'
30 global-exclude .dircopy.log
29 global-exclude .dircopy.log
31 global-exclude .svn
30 global-exclude .svn
32 global-exclude .bzr
31 global-exclude .bzr
32 global-exclude .hgignore
@@ -1,11 +1,11 b''
1 ===============
1 ==============
2 IPython1 README
2 IPython README
3 ===============
3 ==============
4
5 .. contents::
6
4
7 Overview
5 Overview
8 ========
6 ========
9
7
10 Welcome to IPython. New users should consult our documentation, which can be found
8 Welcome to IPython. Our documentation can be found in the docs/source
11 in the docs/source subdirectory.
9 subdirectory. We also have ``.html`` and ``.pdf`` versions of this
10 documentation available on the IPython `website <http://ipython.scipy.org>`_.
11
@@ -28,17 +28,16 b' help:'
28 @echo "dist all, and then puts the results in dist/"
28 @echo "dist all, and then puts the results in dist/"
29
29
30 clean:
30 clean:
31 -rm -rf build/*
31 -rm -rf build/* dist/*
32
32
33 pdf: latex
33 pdf: latex
34 cd build/latex && make all-pdf
34 cd build/latex && make all-pdf
35
35
36 all: html pdf
36 all: html pdf
37
37
38 dist: all
38 dist: clean all
39 mkdir -p dist
39 mkdir -p dist
40 -rm -rf dist/*
40 ln build/latex/ipython.pdf dist/
41 ln build/latex/IPython.pdf dist/
42 cp -al build/html dist/
41 cp -al build/html dist/
43 @echo "Build finished. Final docs are in dist/"
42 @echo "Build finished. Final docs are in dist/"
44
43
@@ -40,3 +40,4 b' def main():'
40
40
41 if __name__ == '__main__':
41 if __name__ == '__main__':
42 main()
42 main()
43
@@ -45,7 +45,10 b' def mergesort(list_of_lists, key=None):'
45 for i, itr in enumerate(iter(pl) for pl in list_of_lists):
45 for i, itr in enumerate(iter(pl) for pl in list_of_lists):
46 try:
46 try:
47 item = itr.next()
47 item = itr.next()
48 toadd = (key(item), i, item, itr) if key else (item, i, itr)
48 if key:
49 toadd = (key(item), i, item, itr)
50 else:
51 toadd = (item, i, itr)
49 heap.append(toadd)
52 heap.append(toadd)
50 except StopIteration:
53 except StopIteration:
51 pass
54 pass
@@ -53,5 +53,5 b" if __name__ == '__main__':"
53 %timeit -n1 -r1 parallelDiffs(rc, nmats, matsize)
53 %timeit -n1 -r1 parallelDiffs(rc, nmats, matsize)
54
54
55 # Uncomment these to plot the histogram
55 # Uncomment these to plot the histogram
56 import pylab
56 # import pylab
57 pylab.hist(parallelDiffs(rc,matsize,matsize))
57 # pylab.hist(parallelDiffs(rc,matsize,matsize))
@@ -5,6 +5,89 b" What's new"
5 ==========
5 ==========
6
6
7 .. contents::
7 .. contents::
8 ..
9 1 Release 0.9.1
10 2 Release 0.9
11 2.1 New features
12 2.2 Bug fixes
13 2.3 Backwards incompatible changes
14 2.4 Changes merged in from IPython1
15 2.4.1 New features
16 2.4.2 Bug fixes
17 2.4.3 Backwards incompatible changes
18 3 Release 0.8.4
19 4 Release 0.8.3
20 5 Release 0.8.2
21 6 Older releases
22 ..
23
24 Release dev
25 ===========
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
47 * cd completer: show bookmarks if no other completions are available.
48
49 * sh profile: easy way to give 'title' to prompt: assign to variable
50 '_prompt_title'. It looks like this::
51
52 [~]|1> _prompt_title = 'sudo!'
53 sudo![~]|2>
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
65 * %rehashx: Aliases no longer contain dots. python3.0 binary
66 will create alias python30. Fixes:
67 #259716 "commands with dots in them don't work"
68
69 * %cpaste: %cpaste -r repeats the last pasted block.
70 The block is assigned to pasted_block even if code
71 raises exception.
72
73 Backwards incompatible changes
74 ------------------------------
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
82
83
84 Release 0.9.1
85 =============
86
87 This release was quickly made to restore compatibility with Python 2.4, which
88 version 0.9 accidentally broke. No new features were introduced, other than
89 some additional testing support for internal use.
90
8
91
9 Release 0.9
92 Release 0.9
10 ===========
93 ===========
@@ -12,89 +95,170 b' Release 0.9'
12 New features
95 New features
13 ------------
96 ------------
14
97
15 * The notion of a task has been completely reworked. An `ITask` interface has
98 * All furl files and security certificates are now put in a read-only
16 been created. This interface defines the methods that tasks need to implement.
99 directory named ~./ipython/security.
17 These methods are now responsible for things like submitting tasks and processing
100
18 results. There are two basic task types: :class:`IPython.kernel.task.StringTask`
101 * A single function :func:`get_ipython_dir`, in :mod:`IPython.genutils` that
19 (this is the old `Task` object, but renamed) and the new
102 determines the user's IPython directory in a robust manner.
20 :class:`IPython.kernel.task.MapTask`, which is based on a function.
103
21 * A new interface, :class:`IPython.kernel.mapper.IMapper` has been defined to
104 * Laurent's WX application has been given a top-level script called
22 standardize the idea of a `map` method. This interface has a single
105 ipython-wx, and it has received numerous fixes. We expect this code to be
23 `map` method that has the same syntax as the built-in `map`. We have also defined
106 architecturally better integrated with Gael's WX 'ipython widget' over the
24 a `mapper` factory interface that creates objects that implement
107 next few releases.
25 :class:`IPython.kernel.mapper.IMapper` for different controllers. Both
108
26 the multiengine and task controller now have mapping capabilties.
109 * The Editor synchronization work by Vivian De Smedt has been merged in. This
27 * The parallel function capabilities have been reworks. The major changes are that
110 code adds a number of new editor hooks to synchronize with editors under
28 i) there is now an `@parallel` magic that creates parallel functions, ii)
111 Windows.
29 the syntax for mulitple variable follows that of `map`, iii) both the
112
30 multiengine and task controller now have a parallel function implementation.
113 * A new, still experimental but highly functional, WX shell by Gael Varoquaux.
31 * All of the parallel computing capabilities from `ipython1-dev` have been merged into
114 This work was sponsored by Enthought, and while it's still very new, it is
32 IPython proper. This resulted in the following new subpackages:
115 based on a more cleanly organized arhictecture of the various IPython
33 :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`,
116 components. We will continue to develop this over the next few releases as a
34 :mod:`IPython.tools` and :mod:`IPython.testing`.
117 model for GUI components that use IPython.
35 * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and friends
118
36 have been completely refactored. Now we are checking for dependencies using
119 * Another GUI frontend, Cocoa based (Cocoa is the OSX native GUI framework),
37 the approach that matplotlib uses.
120 authored by Barry Wark. Currently the WX and the Cocoa ones have slightly
38 * The documentation has been completely reorganized to accept the documentation
121 different internal organizations, but the whole team is working on finding
39 from `ipython1-dev`.
122 what the right abstraction points are for a unified codebase.
40 * We have switched to using Foolscap for all of our network protocols in
123
41 :mod:`IPython.kernel`. This gives us secure connections that are both encrypted
124 * As part of the frontend work, Barry Wark also implemented an experimental
42 and authenticated.
125 event notification system that various ipython components can use. In the
43 * We have a brand new `COPYING.txt` files that describes the IPython license
126 next release the implications and use patterns of this system regarding the
44 and copyright. The biggest change is that we are putting "The IPython
127 various GUI options will be worked out.
45 Development Team" as the copyright holder. We give more details about exactly
128
46 what this means in this file. All developer should read this and use the new
129 * IPython finally has a full test system, that can test docstrings with
47 banner in all IPython source code files.
130 IPython-specific functionality. There are still a few pieces missing for it
48 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
131 to be widely accessible to all users (so they can run the test suite at any
49 * String lists now support 'sort(field, nums = True)' method (to easily
132 time and report problems), but it now works for the developers. We are
50 sort system command output). Try it with 'a = !ls -l ; a.sort(1, nums=1)'
133 working hard on continuing to improve it, as this was probably IPython's
51 * '%cpaste foo' now assigns the pasted block as string list, instead of string
134 major Achilles heel (the lack of proper test coverage made it effectively
52 * The ipcluster script now run by default with no security. This is done because
135 impossible to do large-scale refactoring). The full test suite can now
53 the main usage of the script is for starting things on localhost. Eventually
136 be run using the :command:`iptest` command line program.
54 when ipcluster is able to start things on other hosts, we will put security
137
55 back.
138 * The notion of a task has been completely reworked. An `ITask` interface has
56 * 'cd --foo' searches directory history for string foo, and jumps to that dir.
139 been created. This interface defines the methods that tasks need to
57 Last part of dir name is checked first. If no matches for that are found,
140 implement. These methods are now responsible for things like submitting
58 look at the whole path.
141 tasks and processing results. There are two basic task types:
142 :class:`IPython.kernel.task.StringTask` (this is the old `Task` object, but
143 renamed) and the new :class:`IPython.kernel.task.MapTask`, which is based on
144 a function.
145
146 * A new interface, :class:`IPython.kernel.mapper.IMapper` has been defined to
147 standardize the idea of a `map` method. This interface has a single `map`
148 method that has the same syntax as the built-in `map`. We have also defined
149 a `mapper` factory interface that creates objects that implement
150 :class:`IPython.kernel.mapper.IMapper` for different controllers. Both the
151 multiengine and task controller now have mapping capabilties.
152
153 * The parallel function capabilities have been reworks. The major changes are
154 that i) there is now an `@parallel` magic that creates parallel functions,
155 ii) the syntax for mulitple variable follows that of `map`, iii) both the
156 multiengine and task controller now have a parallel function implementation.
59
157
158 * All of the parallel computing capabilities from `ipython1-dev` have been
159 merged into IPython proper. This resulted in the following new subpackages:
160 :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`,
161 :mod:`IPython.tools` and :mod:`IPython.testing`.
162
163 * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and
164 friends have been completely refactored. Now we are checking for
165 dependencies using the approach that matplotlib uses.
166
167 * The documentation has been completely reorganized to accept the
168 documentation from `ipython1-dev`.
169
170 * We have switched to using Foolscap for all of our network protocols in
171 :mod:`IPython.kernel`. This gives us secure connections that are both
172 encrypted and authenticated.
173
174 * We have a brand new `COPYING.txt` files that describes the IPython license
175 and copyright. The biggest change is that we are putting "The IPython
176 Development Team" as the copyright holder. We give more details about
177 exactly what this means in this file. All developer should read this and use
178 the new banner in all IPython source code files.
179
180 * sh profile: ./foo runs foo as system command, no need to do !./foo anymore
181
182 * String lists now support ``sort(field, nums = True)`` method (to easily sort
183 system command output). Try it with ``a = !ls -l ; a.sort(1, nums=1)``.
184
185 * '%cpaste foo' now assigns the pasted block as string list, instead of string
186
187 * The ipcluster script now run by default with no security. This is done
188 because the main usage of the script is for starting things on localhost.
189 Eventually when ipcluster is able to start things on other hosts, we will put
190 security back.
191
192 * 'cd --foo' searches directory history for string foo, and jumps to that dir.
193 Last part of dir name is checked first. If no matches for that are found,
194 look at the whole path.
195
196
60 Bug fixes
197 Bug fixes
61 ---------
198 ---------
62
199
63 * The colors escapes in the multiengine client are now turned off on win32 as they
200 * The Windows installer has been fixed. Now all IPython scripts have ``.bat``
64 don't print correctly.
201 versions created. Also, the Start Menu shortcuts have been updated.
65 * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing mpi_import_statement
202
66 incorrectly, which was leading the engine to crash when mpi was enabled.
203 * The colors escapes in the multiengine client are now turned off on win32 as
67 * A few subpackages has missing `__init__.py` files.
204 they don't print correctly.
68 * The documentation is only created is Sphinx is found. Previously, the `setup.py`
205
69 script would fail if it was missing.
206 * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing
70 * Greedy 'cd' completion has been disabled again (it was enabled in 0.8.4)
207 mpi_import_statement incorrectly, which was leading the engine to crash when
208 mpi was enabled.
209
210 * A few subpackages had missing ``__init__.py`` files.
211
212 * The documentation is only created if Sphinx is found. Previously, the
213 ``setup.py`` script would fail if it was missing.
214
215 * Greedy ``cd`` completion has been disabled again (it was enabled in 0.8.4) as
216 it caused problems on certain platforms.
71
217
72
218
73 Backwards incompatible changes
219 Backwards incompatible changes
74 ------------------------------
220 ------------------------------
75
221
76 * :class:`IPython.kernel.client.Task` has been renamed
222 * The ``clusterfile`` options of the :command:`ipcluster` command has been
77 :class:`IPython.kernel.client.StringTask` to make way for new task types.
223 removed as it was not working and it will be replaced soon by something much
78 * The keyword argument `style` has been renamed `dist` in `scatter`, `gather`
224 more robust.
79 and `map`.
225
80 * Renamed the values that the rename `dist` keyword argument can have from
226 * The :mod:`IPython.kernel` configuration now properly find the user's
81 `'basic'` to `'b'`.
227 IPython directory.
82 * IPython has a larger set of dependencies if you want all of its capabilities.
228
83 See the `setup.py` script for details.
229 * In ipapi, the :func:`make_user_ns` function has been replaced with
84 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and
230 :func:`make_user_namespaces`, to support dict subclasses in namespace
85 :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple.
231 creation.
86 Instead they take the filename of a file that contains the FURL for that
232
87 client. If the FURL file is in your IPYTHONDIR, it will be found automatically
233 * :class:`IPython.kernel.client.Task` has been renamed
88 and the constructor can be left empty.
234 :class:`IPython.kernel.client.StringTask` to make way for new task types.
89 * The asynchronous clients in :mod:`IPython.kernel.asyncclient` are now created
235
90 using the factory functions :func:`get_multiengine_client` and
236 * The keyword argument `style` has been renamed `dist` in `scatter`, `gather`
91 :func:`get_task_client`. These return a `Deferred` to the actual client.
237 and `map`.
92 * The command line options to `ipcontroller` and `ipengine` have changed to
238
93 reflect the new Foolscap network protocol and the FURL files. Please see the
239 * Renamed the values that the rename `dist` keyword argument can have from
94 help for these scripts for details.
240 `'basic'` to `'b'`.
95 * The configuration files for the kernel have changed because of the Foolscap stuff.
241
96 If you were using custom config files before, you should delete them and regenerate
242 * IPython has a larger set of dependencies if you want all of its capabilities.
97 new ones.
243 See the `setup.py` script for details.
244
245 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and
246 :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple.
247 Instead they take the filename of a file that contains the FURL for that
248 client. If the FURL file is in your IPYTHONDIR, it will be found automatically
249 and the constructor can be left empty.
250
251 * The asynchronous clients in :mod:`IPython.kernel.asyncclient` are now created
252 using the factory functions :func:`get_multiengine_client` and
253 :func:`get_task_client`. These return a `Deferred` to the actual client.
254
255 * The command line options to `ipcontroller` and `ipengine` have changed to
256 reflect the new Foolscap network protocol and the FURL files. Please see the
257 help for these scripts for details.
258
259 * The configuration files for the kernel have changed because of the Foolscap
260 stuff. If you were using custom config files before, you should delete them
261 and regenerate new ones.
98
262
99 Changes merged in from IPython1
263 Changes merged in from IPython1
100 -------------------------------
264 -------------------------------
@@ -102,89 +266,109 b' Changes merged in from IPython1'
102 New features
266 New features
103 ............
267 ............
104
268
105 * Much improved ``setup.py`` and ``setupegg.py`` scripts. Because Twisted
269 * Much improved ``setup.py`` and ``setupegg.py`` scripts. Because Twisted and
106 and zope.interface are now easy installable, we can declare them as dependencies
270 zope.interface are now easy installable, we can declare them as dependencies
107 in our setupegg.py script.
271 in our setupegg.py script.
108 * IPython is now compatible with Twisted 2.5.0 and 8.x.
272
109 * Added a new example of how to use :mod:`ipython1.kernel.asynclient`.
273 * IPython is now compatible with Twisted 2.5.0 and 8.x.
110 * Initial draft of a process daemon in :mod:`ipython1.daemon`. This has not
274
111 been merged into IPython and is still in `ipython1-dev`.
275 * Added a new example of how to use :mod:`ipython1.kernel.asynclient`.
112 * The ``TaskController`` now has methods for getting the queue status.
276
113 * The ``TaskResult`` objects not have information about how long the task
277 * Initial draft of a process daemon in :mod:`ipython1.daemon`. This has not
114 took to run.
278 been merged into IPython and is still in `ipython1-dev`.
115 * We are attaching additional attributes to exceptions ``(_ipython_*)`` that
279
116 we use to carry additional info around.
280 * The ``TaskController`` now has methods for getting the queue status.
117 * New top-level module :mod:`asyncclient` that has asynchronous versions (that
281
118 return deferreds) of the client classes. This is designed to users who want
282 * The ``TaskResult`` objects not have information about how long the task
119 to run their own Twisted reactor
283 took to run.
120 * All the clients in :mod:`client` are now based on Twisted. This is done by
284
121 running the Twisted reactor in a separate thread and using the
285 * We are attaching additional attributes to exceptions ``(_ipython_*)`` that
122 :func:`blockingCallFromThread` function that is in recent versions of Twisted.
286 we use to carry additional info around.
123 * Functions can now be pushed/pulled to/from engines using
287
124 :meth:`MultiEngineClient.push_function` and :meth:`MultiEngineClient.pull_function`.
288 * New top-level module :mod:`asyncclient` that has asynchronous versions (that
125 * Gather/scatter are now implemented in the client to reduce the work load
289 return deferreds) of the client classes. This is designed to users who want
126 of the controller and improve performance.
290 to run their own Twisted reactor.
127 * Complete rewrite of the IPython docuementation. All of the documentation
291
128 from the IPython website has been moved into docs/source as restructured
292 * All the clients in :mod:`client` are now based on Twisted. This is done by
129 text documents. PDF and HTML documentation are being generated using
293 running the Twisted reactor in a separate thread and using the
130 Sphinx.
294 :func:`blockingCallFromThread` function that is in recent versions of Twisted.
131 * New developer oriented documentation: development guidelines and roadmap.
295
132 * Traditional ``ChangeLog`` has been changed to a more useful ``changes.txt`` file
296 * Functions can now be pushed/pulled to/from engines using
133 that is organized by release and is meant to provide something more relevant
297 :meth:`MultiEngineClient.push_function` and
134 for users.
298 :meth:`MultiEngineClient.pull_function`.
299
300 * Gather/scatter are now implemented in the client to reduce the work load
301 of the controller and improve performance.
302
303 * Complete rewrite of the IPython docuementation. All of the documentation
304 from the IPython website has been moved into docs/source as restructured
305 text documents. PDF and HTML documentation are being generated using
306 Sphinx.
307
308 * New developer oriented documentation: development guidelines and roadmap.
309
310 * Traditional ``ChangeLog`` has been changed to a more useful ``changes.txt``
311 file that is organized by release and is meant to provide something more
312 relevant for users.
135
313
136 Bug fixes
314 Bug fixes
137 .........
315 .........
138
316
139 * Created a proper ``MANIFEST.in`` file to create source distributions.
317 * Created a proper ``MANIFEST.in`` file to create source distributions.
140 * Fixed a bug in the ``MultiEngine`` interface. Previously, multi-engine
318
141 actions were being collected with a :class:`DeferredList` with
319 * Fixed a bug in the ``MultiEngine`` interface. Previously, multi-engine
142 ``fireononeerrback=1``. This meant that methods were returning
320 actions were being collected with a :class:`DeferredList` with
143 before all engines had given their results. This was causing extremely odd
321 ``fireononeerrback=1``. This meant that methods were returning
144 bugs in certain cases. To fix this problem, we have 1) set
322 before all engines had given their results. This was causing extremely odd
145 ``fireononeerrback=0`` to make sure all results (or exceptions) are in
323 bugs in certain cases. To fix this problem, we have 1) set
146 before returning and 2) introduced a :exc:`CompositeError` exception
324 ``fireononeerrback=0`` to make sure all results (or exceptions) are in
147 that wraps all of the engine exceptions. This is a huge change as it means
325 before returning and 2) introduced a :exc:`CompositeError` exception
148 that users will have to catch :exc:`CompositeError` rather than the actual
326 that wraps all of the engine exceptions. This is a huge change as it means
149 exception.
327 that users will have to catch :exc:`CompositeError` rather than the actual
328 exception.
150
329
151 Backwards incompatible changes
330 Backwards incompatible changes
152 ..............................
331 ..............................
153
332
154 * All names have been renamed to conform to the lowercase_with_underscore
333 * All names have been renamed to conform to the lowercase_with_underscore
155 convention. This will require users to change references to all names like
334 convention. This will require users to change references to all names like
156 ``queueStatus`` to ``queue_status``.
335 ``queueStatus`` to ``queue_status``.
157 * Previously, methods like :meth:`MultiEngineClient.push` and
336
158 :meth:`MultiEngineClient.push` used ``*args`` and ``**kwargs``. This was
337 * Previously, methods like :meth:`MultiEngineClient.push` and
159 becoming a problem as we weren't able to introduce new keyword arguments into
338 :meth:`MultiEngineClient.push` used ``*args`` and ``**kwargs``. This was
160 the API. Now these methods simple take a dict or sequence. This has also allowed
339 becoming a problem as we weren't able to introduce new keyword arguments into
161 us to get rid of the ``*All`` methods like :meth:`pushAll` and :meth:`pullAll`.
340 the API. Now these methods simple take a dict or sequence. This has also
162 These things are now handled with the ``targets`` keyword argument that defaults
341 allowed us to get rid of the ``*All`` methods like :meth:`pushAll` and
163 to ``'all'``.
342 :meth:`pullAll`. These things are now handled with the ``targets`` keyword
164 * The :attr:`MultiEngineClient.magicTargets` has been renamed to
343 argument that defaults to ``'all'``.
165 :attr:`MultiEngineClient.targets`.
344
166 * All methods in the MultiEngine interface now accept the optional keyword argument
345 * The :attr:`MultiEngineClient.magicTargets` has been renamed to
167 ``block``.
346 :attr:`MultiEngineClient.targets`.
168 * Renamed :class:`RemoteController` to :class:`MultiEngineClient` and
347
169 :class:`TaskController` to :class:`TaskClient`.
348 * All methods in the MultiEngine interface now accept the optional keyword
170 * Renamed the top-level module from :mod:`api` to :mod:`client`.
349 argument ``block``.
171 * Most methods in the multiengine interface now raise a :exc:`CompositeError` exception
350
172 that wraps the user's exceptions, rather than just raising the raw user's exception.
351 * Renamed :class:`RemoteController` to :class:`MultiEngineClient` and
173 * Changed the ``setupNS`` and ``resultNames`` in the ``Task`` class to ``push``
352 :class:`TaskController` to :class:`TaskClient`.
174 and ``pull``.
353
354 * Renamed the top-level module from :mod:`api` to :mod:`client`.
175
355
356 * Most methods in the multiengine interface now raise a :exc:`CompositeError`
357 exception that wraps the user's exceptions, rather than just raising the raw
358 user's exception.
359
360 * Changed the ``setupNS`` and ``resultNames`` in the ``Task`` class to ``push``
361 and ``pull``.
362
363
176 Release 0.8.4
364 Release 0.8.4
177 =============
365 =============
178
366
179 Someone needs to describe what went into 0.8.4.
367 This was a quick release to fix an unfortunate bug that slipped into the 0.8.3
368 release. The ``--twisted`` option was disabled, as it turned out to be broken
369 across several platforms.
180
370
181 Release 0.8.2
182 =============
183
371
184 * %pushd/%popd behave differently; now "pushd /foo" pushes CURRENT directory
185 and jumps to /foo. The current behaviour is closer to the documented
186 behaviour, and should not trip anyone.
187
188 Release 0.8.3
372 Release 0.8.3
189 =============
373 =============
190
374
@@ -192,9 +376,18 b' Release 0.8.3'
192 it by passing -pydb command line argument to IPython. Note that setting
376 it by passing -pydb command line argument to IPython. Note that setting
193 it in config file won't work.
377 it in config file won't work.
194
378
379
380 Release 0.8.2
381 =============
382
383 * %pushd/%popd behave differently; now "pushd /foo" pushes CURRENT directory
384 and jumps to /foo. The current behaviour is closer to the documented
385 behaviour, and should not trip anyone.
386
387
195 Older releases
388 Older releases
196 ==============
389 ==============
197
390
198 Changes in earlier releases of IPython are described in the older file ``ChangeLog``.
391 Changes in earlier releases of IPython are described in the older file
199 Please refer to this document for details.
392 ``ChangeLog``. Please refer to this document for details.
200
393
@@ -1,7 +1,11 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 #
2 #
3 # IPython documentation build configuration file, created by
3 # IPython documentation build configuration file.
4 # sphinx-quickstart on Thu May 8 16:45:02 2008.
4
5 # NOTE: This file has been edited manually from the auto-generated one from
6 # sphinx. Do NOT delete and re-generate. If any changes from sphinx are
7 # needed, generate a scratch one and merge by hand any new fields needed.
8
5 #
9 #
6 # This file is execfile()d with the current directory set to its containing dir.
10 # This file is execfile()d with the current directory set to its containing dir.
7 #
11 #
@@ -16,14 +20,26 b' import sys, os'
16 # If your extensions are in another directory, add it here. If the directory
20 # If your extensions are in another directory, add it here. If the directory
17 # is relative to the documentation root, use os.path.abspath to make it
21 # is relative to the documentation root, use os.path.abspath to make it
18 # absolute, like shown here.
22 # absolute, like shown here.
19 #sys.path.append(os.path.abspath('some/directory'))
23 sys.path.append(os.path.abspath('../sphinxext'))
24
25 # Import support for ipython console session syntax highlighting (lives
26 # in the sphinxext directory defined above)
27 import ipython_console_highlighting
28
29 # We load the ipython release info into a dict by explicit execution
30 iprelease = {}
31 execfile('../../IPython/Release.py',iprelease)
20
32
21 # General configuration
33 # General configuration
22 # ---------------------
34 # ---------------------
23
35
24 # Add any Sphinx extension module names here, as strings. They can be extensions
36 # Add any Sphinx extension module names here, as strings. They can be extensions
25 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
37 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
26 #extensions = []
38 extensions = ['sphinx.ext.autodoc',
39 'inheritance_diagram', 'only_directives',
40 'ipython_console_highlighting',
41 # 'plot_directive', # disabled for now, needs matplotlib
42 ]
27
43
28 # Add any paths that contain templates here, relative to this directory.
44 # Add any paths that contain templates here, relative to this directory.
29 templates_path = ['_templates']
45 templates_path = ['_templates']
@@ -41,10 +57,11 b" copyright = '2008, The IPython Development Team'"
41 # The default replacements for |version| and |release|, also used in various
57 # The default replacements for |version| and |release|, also used in various
42 # other places throughout the built documents.
58 # other places throughout the built documents.
43 #
59 #
44 # The short X.Y version.
45 version = '0.9'
46 # The full version, including alpha/beta/rc tags.
60 # The full version, including alpha/beta/rc tags.
47 release = '0.9.beta1'
61 release = iprelease['version']
62 # The short X.Y version.
63 version = '.'.join(release.split('.',2)[:2])
64
48
65
49 # There are two options for replacing |today|: either, you set today to some
66 # There are two options for replacing |today|: either, you set today to some
50 # non-false value, then it is used:
67 # non-false value, then it is used:
@@ -57,7 +74,7 b" today_fmt = '%B %d, %Y'"
57
74
58 # List of directories, relative to source directories, that shouldn't be searched
75 # List of directories, relative to source directories, that shouldn't be searched
59 # for source files.
76 # for source files.
60 #exclude_dirs = []
77 exclude_dirs = ['attic']
61
78
62 # If true, '()' will be appended to :func: etc. cross-reference text.
79 # If true, '()' will be appended to :func: etc. cross-reference text.
63 #add_function_parentheses = True
80 #add_function_parentheses = True
@@ -125,7 +142,7 b" html_last_updated_fmt = '%b %d, %Y'"
125 #html_file_suffix = ''
142 #html_file_suffix = ''
126
143
127 # Output file base name for HTML help builder.
144 # Output file base name for HTML help builder.
128 htmlhelp_basename = 'IPythondoc'
145 htmlhelp_basename = 'ipythondoc'
129
146
130
147
131 # Options for LaTeX output
148 # Options for LaTeX output
@@ -140,11 +157,8 b" latex_font_size = '11pt'"
140 # Grouping the document tree into LaTeX files. List of tuples
157 # Grouping the document tree into LaTeX files. List of tuples
141 # (source start file, target name, title, author, document class [howto/manual]).
158 # (source start file, target name, title, author, document class [howto/manual]).
142
159
143 latex_documents = [ ('index', 'IPython.tex', 'IPython Documentation',
160 latex_documents = [ ('index', 'ipython.tex', 'IPython Documentation',
144 ur"""Brian Granger, Fernando Pérez and Ville Vainio\\
161 ur"""The IPython Development Team""",
145 \ \\
146 With contributions from:\\
147 Benjamin Ragan-Kelley.""",
148 'manual'),
162 'manual'),
149 ]
163 ]
150
164
@@ -164,3 +178,10 b" latex_documents = [ ('index', 'IPython.tex', 'IPython Documentation',"
164
178
165 # If false, no module index is generated.
179 # If false, no module index is generated.
166 #latex_use_modindex = True
180 #latex_use_modindex = True
181
182
183 # Cleanup
184 # -------
185 # delete release info to avoid pickling errors from sphinx
186
187 del iprelease
@@ -18,6 +18,8 b' time. A hybrid approach of specifying a few options in ipythonrc and'
18 doing the more advanced configuration in ipy_user_conf.py is also
18 doing the more advanced configuration in ipy_user_conf.py is also
19 possible.
19 possible.
20
20
21 .. _ipythonrc:
22
21 The ipythonrc approach
23 The ipythonrc approach
22 ======================
24 ======================
23
25
@@ -36,11 +38,11 b' fairly primitive). Note that these are not python files, and this is'
36 deliberate, because it allows us to do some things which would be quite
38 deliberate, because it allows us to do some things which would be quite
37 tricky to implement if they were normal python files.
39 tricky to implement if they were normal python files.
38
40
39 First, an rcfile can contain permanent default values for almost all
41 First, an rcfile can contain permanent default values for almost all command
40 command line options (except things like -help or -Version). Sec
42 line options (except things like -help or -Version). :ref:`This section
41 `command line options`_ contains a description of all command-line
43 <command_line_options>` contains a description of all command-line
42 options. However, values you explicitly specify at the command line
44 options. However, values you explicitly specify at the command line override
43 override the values defined in the rcfile.
45 the values defined in the rcfile.
44
46
45 Besides command line option values, the rcfile can specify values for
47 Besides command line option values, the rcfile can specify values for
46 certain extra special options which are not available at the command
48 certain extra special options which are not available at the command
@@ -266,13 +268,13 b' which look like this::'
266 IPython profiles
268 IPython profiles
267 ================
269 ================
268
270
269 As we already mentioned, IPython supports the -profile command-line
271 As we already mentioned, IPython supports the -profile command-line option (see
270 option (see sec. `command line options`_). A profile is nothing more
272 :ref:`here <command_line_options>`). A profile is nothing more than a
271 than a particular configuration file like your basic ipythonrc one,
273 particular configuration file like your basic ipythonrc one, but with
272 but with particular customizations for a specific purpose. When you
274 particular customizations for a specific purpose. When you start IPython with
273 start IPython with 'ipython -profile <name>', it assumes that in your
275 'ipython -profile <name>', it assumes that in your IPYTHONDIR there is a file
274 IPYTHONDIR there is a file called ipythonrc-<name> or
276 called ipythonrc-<name> or ipy_profile_<name>.py, and loads it instead of the
275 ipy_profile_<name>.py, and loads it instead of the normal ipythonrc.
277 normal ipythonrc.
276
278
277 This system allows you to maintain multiple configurations which load
279 This system allows you to maintain multiple configurations which load
278 modules, set options, define functions, etc. suitable for different
280 modules, set options, define functions, etc. suitable for different
@@ -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
@@ -11,28 +11,27 b' in a directory named by default $HOME/.ipython. You can change this by'
11 defining the environment variable IPYTHONDIR, or at runtime with the
11 defining the environment variable IPYTHONDIR, or at runtime with the
12 command line option -ipythondir.
12 command line option -ipythondir.
13
13
14 If all goes well, the first time you run IPython it should
14 If all goes well, the first time you run IPython it should automatically create
15 automatically create a user copy of the config directory for you,
15 a user copy of the config directory for you, based on its builtin defaults. You
16 based on its builtin defaults. You can look at the files it creates to
16 can look at the files it creates to learn more about configuring the
17 learn more about configuring the system. The main file you will modify
17 system. The main file you will modify to configure IPython's behavior is called
18 to configure IPython's behavior is called ipythonrc (with a .ini
18 ipythonrc (with a .ini extension under Windows), included for reference
19 extension under Windows), included for reference in `ipythonrc`_
19 :ref:`here <ipythonrc>`. This file is very commented and has many variables you
20 section. This file is very commented and has many variables you can
20 can change to suit your taste, you can find more details :ref:`here
21 change to suit your taste, you can find more details in
21 <customization>`. Here we discuss the basic things you will want to make sure
22 Sec. customization_. Here we discuss the basic things you will want to
22 things are working properly from the beginning.
23 make sure things are working properly from the beginning.
24
23
25
24
26 .. _Accessing help:
25 .. _accessing_help:
27
26
28 Access to the Python help system
27 Access to the Python help system
29 ================================
28 ================================
30
29
31 This is true for Python in general (not just for IPython): you should
30 This is true for Python in general (not just for IPython): you should have an
32 have an environment variable called PYTHONDOCS pointing to the directory
31 environment variable called PYTHONDOCS pointing to the directory where your
33 where your HTML Python documentation lives. In my system it's
32 HTML Python documentation lives. In my system it's
34 /usr/share/doc/python-docs-2.3.4/html, check your local details or ask
33 :file:`/usr/share/doc/python-doc/html`, check your local details or ask your
35 your systems administrator.
34 systems administrator.
36
35
37 This is the directory which holds the HTML version of the Python
36 This is the directory which holds the HTML version of the Python
38 manuals. Unfortunately it seems that different Linux distributions
37 manuals. Unfortunately it seems that different Linux distributions
@@ -40,8 +39,9 b' package these files differently, so you may have to look around a bit.'
40 Below I show the contents of this directory on my system for reference::
39 Below I show the contents of this directory on my system for reference::
41
40
42 [html]> ls
41 [html]> ls
43 about.dat acks.html dist/ ext/ index.html lib/ modindex.html
42 about.html dist/ icons/ lib/ python2.5.devhelp.gz whatsnew/
44 stdabout.dat tut/ about.html api/ doc/ icons/ inst/ mac/ ref/ style.css
43 acks.html doc/ index.html mac/ ref/
44 api/ ext/ inst/ modindex.html tut/
45
45
46 You should really make sure this variable is correctly set so that
46 You should really make sure this variable is correctly set so that
47 Python's pydoc-based help system works. It is a powerful and convenient
47 Python's pydoc-based help system works. It is a powerful and convenient
@@ -108,6 +108,8 b' The following terminals seem to handle the color sequences fine:'
108 support under cygwin, please post to the IPython mailing list so
108 support under cygwin, please post to the IPython mailing list so
109 this issue can be resolved for all users.
109 this issue can be resolved for all users.
110
110
111 .. _pyreadline: https://code.launchpad.net/pyreadline
112
111 These have shown problems:
113 These have shown problems:
112
114
113 * Windows command prompt in WinXP/2k logged into a Linux machine via
115 * Windows command prompt in WinXP/2k logged into a Linux machine via
@@ -157,13 +159,12 b' $HOME/.ipython/ipythonrc and set the colors option to the desired value.'
157 Object details (types, docstrings, source code, etc.)
159 Object details (types, docstrings, source code, etc.)
158 =====================================================
160 =====================================================
159
161
160 IPython has a set of special functions for studying the objects you
162 IPython has a set of special functions for studying the objects you are working
161 are working with, discussed in detail in Sec. `dynamic object
163 with, discussed in detail :ref:`here <dynamic_object_info>`. But this system
162 information`_. But this system relies on passing information which is
164 relies on passing information which is longer than your screen through a data
163 longer than your screen through a data pager, such as the common Unix
165 pager, such as the common Unix less and more programs. In order to be able to
164 less and more programs. In order to be able to see this information in
166 see this information in color, your pager needs to be properly configured. I
165 color, your pager needs to be properly configured. I strongly
167 strongly recommend using less instead of more, as it seems that more simply can
166 recommend using less instead of more, as it seems that more simply can
167 not understand colored text correctly.
168 not understand colored text correctly.
168
169
169 In order to configure less as your default pager, do the following:
170 In order to configure less as your default pager, do the following:
@@ -4,24 +4,39 b''
4 Credits
4 Credits
5 =======
5 =======
6
6
7 IPython is mainly developed by Fernando Pérez
7 IPython is led by Fernando Pérez.
8 <Fernando.Perez@colorado.edu>, but the project was born from mixing in
8
9 Fernando's code with the IPP project by Janko Hauser
9 As of this writing, the following developers have joined the core team:
10 <jhauser-AT-zscout.de> and LazyPython by Nathan Gray
10
11 <n8gray-AT-caltech.edu>. For all IPython-related requests, please
11 * [Robert Kern] <rkern-AT-enthought.com>: co-mentored the 2005
12 contact Fernando.
12 Google Summer of Code project to develop python interactive
13
13 notebooks (XML documents) and graphical interface. This project
14 As of early 2006, the following developers have joined the core team:
14 was awarded to the students Tzanko Matev <tsanko-AT-gmail.com> and
15
15 Toni Alatalo <antont-AT-an.org>.
16 * [Robert Kern] <rkern-AT-enthought.com>: co-mentored the 2005
16
17 Google Summer of Code project to develop python interactive
17 * [Brian Granger] <ellisonbg-AT-gmail.com>: extending IPython to allow
18 notebooks (XML documents) and graphical interface. This project
18 support for interactive parallel computing.
19 was awarded to the students Tzanko Matev <tsanko-AT-gmail.com> and
19
20 Toni Alatalo <antont-AT-an.org>
20 * [Benjamin (Min) Ragan-Kelley]: key work on IPython's parallel
21 * [Brian Granger] <bgranger-AT-scu.edu>: extending IPython to allow
21 computing infrastructure.
22 support for interactive parallel computing.
22
23 * [Ville Vainio] <vivainio-AT-gmail.com>: Ville is the new
23 * [Ville Vainio] <vivainio-AT-gmail.com>: Ville has made many improvements
24 maintainer for the main trunk of IPython after version 0.7.1.
24 to the core of IPython and was the maintainer of the main IPython
25 trunk from version 0.7.1 to 0.8.4.
26
27 * [Gael Varoquaux] <gael.varoquaux-AT-normalesup.org>: work on the merged
28 architecture for the interpreter as of version 0.9, implementing a new WX GUI
29 based on this system.
30
31 * [Barry Wark] <barrywark-AT-gmail.com>: implementing a new Cocoa GUI, as well
32 as work on the new interpreter architecture and Twisted support.
33
34 * [Laurent Dufrechou] <laurent.dufrechou-AT-gmail.com>: development of the WX
35 GUI support.
36
37 * [Jörgen Stenarson] <jorgen.stenarson-AT-bostream.nu>: maintainer of the
38 PyReadline project, necessary for IPython under windows.
39
25
40
26 The IPython project is also very grateful to:
41 The IPython project is also very grateful to:
27
42
@@ -50,90 +65,143 b" an O'Reilly Python editor. His Oct/11/2001 article about IPP and"
50 LazyPython, was what got this project started. You can read it at:
65 LazyPython, was what got this project started. You can read it at:
51 http://www.onlamp.com/pub/a/python/2001/10/11/pythonnews.html.
66 http://www.onlamp.com/pub/a/python/2001/10/11/pythonnews.html.
52
67
53 And last but not least, all the kind IPython users who have emailed new
68 And last but not least, all the kind IPython users who have emailed new code,
54 code, bug reports, fixes, comments and ideas. A brief list follows,
69 bug reports, fixes, comments and ideas. A brief list follows, please let us
55 please let me know if I have ommitted your name by accident:
70 know if we have ommitted your name by accident:
56
71
57 * [Jack Moffit] <jack-AT-xiph.org> Bug fixes, including the infamous
72 * Dan Milstein <danmil-AT-comcast.net>. A bold refactoring of the
58 color problem. This bug alone caused many lost hours and
73 core prefilter stuff in the IPython interpreter.
59 frustration, many thanks to him for the fix. I've always been a
74
60 fan of Ogg & friends, now I have one more reason to like these folks.
75 * [Jack Moffit] <jack-AT-xiph.org> Bug fixes, including the infamous
61 Jack is also contributing with Debian packaging and many other
76 color problem. This bug alone caused many lost hours and
62 things.
77 frustration, many thanks to him for the fix. I've always been a
63 * [Alexander Schmolck] <a.schmolck-AT-gmx.net> Emacs work, bug
78 fan of Ogg & friends, now I have one more reason to like these folks.
64 reports, bug fixes, ideas, lots more. The ipython.el mode for
79 Jack is also contributing with Debian packaging and many other
65 (X)Emacs is Alex's code, providing full support for IPython under
80 things.
66 (X)Emacs.
81
67 * [Andrea Riciputi] <andrea.riciputi-AT-libero.it> Mac OSX
82 * [Alexander Schmolck] <a.schmolck-AT-gmx.net> Emacs work, bug
68 information, Fink package management.
83 reports, bug fixes, ideas, lots more. The ipython.el mode for
69 * [Gary Bishop] <gb-AT-cs.unc.edu> Bug reports, and patches to work
84 (X)Emacs is Alex's code, providing full support for IPython under
70 around the exception handling idiosyncracies of WxPython. Readline
85 (X)Emacs.
71 and color support for Windows.
86
72 * [Jeffrey Collins] <Jeff.Collins-AT-vexcel.com> Bug reports. Much
87 * [Andrea Riciputi] <andrea.riciputi-AT-libero.it> Mac OSX
73 improved readline support, including fixes for Python 2.3.
88 information, Fink package management.
74 * [Dryice Liu] <dryice-AT-liu.com.cn> FreeBSD port.
89
75 * [Mike Heeter] <korora-AT-SDF.LONESTAR.ORG>
90 * [Gary Bishop] <gb-AT-cs.unc.edu> Bug reports, and patches to work
76 * [Christopher Hart] <hart-AT-caltech.edu> PDB integration.
91 around the exception handling idiosyncracies of WxPython. Readline
77 * [Milan Zamazal] <pdm-AT-zamazal.org> Emacs info.
92 and color support for Windows.
78 * [Philip Hisley] <compsys-AT-starpower.net>
93
79 * [Holger Krekel] <pyth-AT-devel.trillke.net> Tab completion, lots
94 * [Jeffrey Collins] <Jeff.Collins-AT-vexcel.com> Bug reports. Much
80 more.
95 improved readline support, including fixes for Python 2.3.
81 * [Robin Siebler] <robinsiebler-AT-starband.net>
96
82 * [Ralf Ahlbrink] <ralf_ahlbrink-AT-web.de>
97 * [Dryice Liu] <dryice-AT-liu.com.cn> FreeBSD port.
83 * [Thorsten Kampe] <thorsten-AT-thorstenkampe.de>
98
84 * [Fredrik Kant] <fredrik.kant-AT-front.com> Windows setup.
99 * [Mike Heeter] <korora-AT-SDF.LONESTAR.ORG>
85 * [Syver Enstad] <syver-en-AT-online.no> Windows setup.
100
86 * [Richard] <rxe-AT-renre-europe.com> Global embedding.
101 * [Christopher Hart] <hart-AT-caltech.edu> PDB integration.
87 * [Hayden Callow] <h.callow-AT-elec.canterbury.ac.nz> Gnuplot.py 1.6
102
88 compatibility.
103 * [Milan Zamazal] <pdm-AT-zamazal.org> Emacs info.
89 * [Leonardo Santagada] <retype-AT-terra.com.br> Fixes for Windows
104
90 installation.
105 * [Philip Hisley] <compsys-AT-starpower.net>
91 * [Christopher Armstrong] <radix-AT-twistedmatrix.com> Bugfixes.
106
92 * [Francois Pinard] <pinard-AT-iro.umontreal.ca> Code and
107 * [Holger Krekel] <pyth-AT-devel.trillke.net> Tab completion, lots
93 documentation fixes.
108 more.
94 * [Cory Dodt] <cdodt-AT-fcoe.k12.ca.us> Bug reports and Windows
109
95 ideas. Patches for Windows installer.
110 * [Robin Siebler] <robinsiebler-AT-starband.net>
96 * [Olivier Aubert] <oaubert-AT-bat710.univ-lyon1.fr> New magics.
111
97 * [King C. Shu] <kingshu-AT-myrealbox.com> Autoindent patch.
112 * [Ralf Ahlbrink] <ralf_ahlbrink-AT-web.de>
98 * [Chris Drexler] <chris-AT-ac-drexler.de> Readline packages for
113
99 Win32/CygWin.
114 * [Thorsten Kampe] <thorsten-AT-thorstenkampe.de>
100 * [Gustavo Cordova Avila] <gcordova-AT-sismex.com> EvalDict code for
115
101 nice, lightweight string interpolation.
116 * [Fredrik Kant] <fredrik.kant-AT-front.com> Windows setup.
102 * [Kasper Souren] <Kasper.Souren-AT-ircam.fr> Bug reports, ideas.
117
103 * [Gever Tulley] <gever-AT-helium.com> Code contributions.
118 * [Syver Enstad] <syver-en-AT-online.no> Windows setup.
104 * [Ralf Schmitt] <ralf-AT-brainbot.com> Bug reports & fixes.
119
105 * [Oliver Sander] <osander-AT-gmx.de> Bug reports.
120 * [Richard] <rxe-AT-renre-europe.com> Global embedding.
106 * [Rod Holland] <rhh-AT-structurelabs.com> Bug reports and fixes to
121
107 logging module.
122 * [Hayden Callow] <h.callow-AT-elec.canterbury.ac.nz> Gnuplot.py 1.6
108 * [Daniel 'Dang' Griffith] <pythondev-dang-AT-lazytwinacres.net>
123 compatibility.
109 Fixes, enhancement suggestions for system shell use.
124
110 * [Viktor Ransmayr] <viktor.ransmayr-AT-t-online.de> Tests and
125 * [Leonardo Santagada] <retype-AT-terra.com.br> Fixes for Windows
111 reports on Windows installation issues. Contributed a true Windows
126 installation.
112 binary installer.
127
113 * [Mike Salib] <msalib-AT-mit.edu> Help fixing a subtle bug related
128 * [Christopher Armstrong] <radix-AT-twistedmatrix.com> Bugfixes.
114 to traceback printing.
129
115 * [W.J. van der Laan] <gnufnork-AT-hetdigitalegat.nl> Bash-like
130 * [Francois Pinard] <pinard-AT-iro.umontreal.ca> Code and
116 prompt specials.
131 documentation fixes.
117 * [Antoon Pardon] <Antoon.Pardon-AT-rece.vub.ac.be> Critical fix for
132
118 the multithreaded IPython.
133 * [Cory Dodt] <cdodt-AT-fcoe.k12.ca.us> Bug reports and Windows
119 * [John Hunter] <jdhunter-AT-nitace.bsd.uchicago.edu> Matplotlib
134 ideas. Patches for Windows installer.
120 author, helped with all the development of support for matplotlib
135
121 in IPyhton, including making necessary changes to matplotlib itself.
136 * [Olivier Aubert] <oaubert-AT-bat710.univ-lyon1.fr> New magics.
122 * [Matthew Arnison] <maffew-AT-cat.org.au> Bug reports, '%run -d' idea.
137
123 * [Prabhu Ramachandran] <prabhu_r-AT-users.sourceforge.net> Help
138 * [King C. Shu] <kingshu-AT-myrealbox.com> Autoindent patch.
124 with (X)Emacs support, threading patches, ideas...
139
125 * [Norbert Tretkowski] <tretkowski-AT-inittab.de> help with Debian
140 * [Chris Drexler] <chris-AT-ac-drexler.de> Readline packages for
126 packaging and distribution.
141 Win32/CygWin.
127 * [George Sakkis] <gsakkis-AT-eden.rutgers.edu> New matcher for
142
128 tab-completing named arguments of user-defined functions.
143 * [Gustavo Cordova Avila] <gcordova-AT-sismex.com> EvalDict code for
129 * [Jörgen Stenarson] <jorgen.stenarson-AT-bostream.nu> Wildcard
144 nice, lightweight string interpolation.
130 support implementation for searching namespaces.
145
131 * [Vivian De Smedt] <vivian-AT-vdesmedt.com> Debugger enhancements,
146 * [Kasper Souren] <Kasper.Souren-AT-ircam.fr> Bug reports, ideas.
132 so that when pdb is activated from within IPython, coloring, tab
147
133 completion and other features continue to work seamlessly.
148 * [Gever Tulley] <gever-AT-helium.com> Code contributions.
134 * [Scott Tsai] <scottt958-AT-yahoo.com.tw> Support for automatic
149
135 editor invocation on syntax errors (see
150 * [Ralf Schmitt] <ralf-AT-brainbot.com> Bug reports & fixes.
136 http://www.scipy.net/roundup/ipython/issue36).
151
137 * [Alexander Belchenko] <bialix-AT-ukr.net> Improvements for win32
152 * [Oliver Sander] <osander-AT-gmx.de> Bug reports.
138 paging system.
153
139 * [Will Maier] <willmaier-AT-ml1.net> Official OpenBSD port. No newline at end of file
154 * [Rod Holland] <rhh-AT-structurelabs.com> Bug reports and fixes to
155 logging module.
156
157 * [Daniel 'Dang' Griffith] <pythondev-dang-AT-lazytwinacres.net>
158 Fixes, enhancement suggestions for system shell use.
159
160 * [Viktor Ransmayr] <viktor.ransmayr-AT-t-online.de> Tests and
161 reports on Windows installation issues. Contributed a true Windows
162 binary installer.
163
164 * [Mike Salib] <msalib-AT-mit.edu> Help fixing a subtle bug related
165 to traceback printing.
166
167 * [W.J. van der Laan] <gnufnork-AT-hetdigitalegat.nl> Bash-like
168 prompt specials.
169
170 * [Antoon Pardon] <Antoon.Pardon-AT-rece.vub.ac.be> Critical fix for
171 the multithreaded IPython.
172
173 * [John Hunter] <jdhunter-AT-nitace.bsd.uchicago.edu> Matplotlib
174 author, helped with all the development of support for matplotlib
175 in IPyhton, including making necessary changes to matplotlib itself.
176
177 * [Matthew Arnison] <maffew-AT-cat.org.au> Bug reports, '%run -d' idea.
178
179 * [Prabhu Ramachandran] <prabhu_r-AT-users.sourceforge.net> Help
180 with (X)Emacs support, threading patches, ideas...
181
182 * [Norbert Tretkowski] <tretkowski-AT-inittab.de> help with Debian
183 packaging and distribution.
184
185 * [George Sakkis] <gsakkis-AT-eden.rutgers.edu> New matcher for
186 tab-completing named arguments of user-defined functions.
187
188 * [Jörgen Stenarson] <jorgen.stenarson-AT-bostream.nu> Wildcard
189 support implementation for searching namespaces.
190
191 * [Vivian De Smedt] <vivian-AT-vdesmedt.com> Debugger enhancements,
192 so that when pdb is activated from within IPython, coloring, tab
193 completion and other features continue to work seamlessly.
194
195 * [Scott Tsai] <scottt958-AT-yahoo.com.tw> Support for automatic
196 editor invocation on syntax errors (see
197 http://www.scipy.net/roundup/ipython/issue36).
198
199 * [Alexander Belchenko] <bialix-AT-ukr.net> Improvements for win32
200 paging system.
201
202 * [Will Maier] <willmaier-AT-ml1.net> Official OpenBSD port.
203
204 * [Ondrej Certik] <ondrej-AT-certik.cz>: set up the IPython docs to use the new
205 Sphinx system used by Python, Matplotlib and many more projects.
206
207 * [Stefan van der Walt] <stefan-AT-sun.ac.za>: support for the new config system.
@@ -1,191 +1,142 b''
1 .. _development:
1 .. _development:
2
2
3 ==================================
3 ==============================
4 IPython development guidelines
4 IPython development guidelines
5 ==================================
5 ==============================
6
7 .. contents::
8
6
9
7
10 Overview
8 Overview
11 ========
9 ========
12
10
13 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
14
12 importantly, it gives information for people who want to contribute to the
15 - Eventually, IPython will become IPython version 1.0.
13 development of IPython. So if you want to help out, read on!
16 - This new code base needs to be able to co-exist with the existing IPython until
14
17 it is a full replacement for it. Thus we needed a different name. We couldn't
15 How to contribute to IPython
18 use ``ipython`` (lowercase) as some files systems are case insensitive.
16 ============================
19
17
20 There are two, no three, main goals of the IPython effort:
18 IPython development is done using Bazaar [Bazaar]_ and Launchpad [Launchpad]_.
21
19 This makes it easy for people to contribute to the development of IPython.
22 1. Clean up the existing codebase and write lots of tests.
20 Here is a sketch of how to get going.
23 2. Separate the core functionality of IPython from the terminal to enable IPython
21
24 to be used from within a variety of GUI applications.
22 Install Bazaar and create a Launchpad account
25 3. Implement a system for interactive parallel computing.
23 ---------------------------------------------
26
27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
28 out that the technologies required for this goal are nearly identical with those
29 required for goal two. This is the main reason the interactive parallel computing
30 capabilities are being put into IPython proper. Currently the third of these goals is
31 furthest along.
32
33 This document describes IPython from the perspective of developers.
34
24
25 First make sure you have installed Bazaar (see their `website
26 <http://bazaar-vcs.org/>`_). To see that Bazaar is installed and knows about
27 you, try the following::
35
28
36 Project organization
29 $ bzr whoami
37 ====================
30 Joe Coder <jcoder@gmail.com>
38
39 Subpackages
40 -----------
41
31
42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
32 This should display your name and email. Next, you will want to create an
43
33 account on the `Launchpad website <http://www.launchpad.net>`_ and setup your
44 - **Dependencies**. One of the most important things to keep in mind in
34 ssh keys. For more information of setting up your ssh keys, see `this link
45 partitioning code amongst subpackages, is that they should be used to cleanly
35 <https://help.launchpad.net/YourAccount/CreatingAnSSHKeyPair>`_.
46 encapsulate dependencies.
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
48 contains all of the tests for that package. For information about writing tests
49 for IPython, see the `Testing System`_ section of this document.
50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
51 that contains the configuration information for the components of the
52 subpackage. For information about how the IPython configuration system
53 works, see the `Configuration System`_ section of this document.
54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
55 contains all of the command line scripts associated with the subpackage.
56
36
57 Installation and dependencies
37 Get the main IPython branch from Launchpad
58 -----------------------------
38 ------------------------------------------
59
60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
62 features that `setuptools`_ has (like namespace packages), the current implementation
63 of `setuptools`_ has performance problems, particularly on shared file systems. In
64 particular, when Python packages are installed on NSF file systems, import times
65 become much too long (up towards 10 seconds).
66
39
67 Because IPython is being used extensively in the context of high performance
40 Now, you can get a copy of the main IPython development branch (we call this
68 computing, where performance is critical but shared file systems are common, we feel
41 the "trunk")::
69 these performance hits are not acceptable. Thus, until the performance problems
70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
71 are hopeful that these problems will be addressed and that we will eventually begin
72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
73 will make the eventual transition to `setuptools`_ as painless as possible.
74
42
75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
43 $ bzr branch lp:ipython
76
44
77 - Distinguish between required and optional dependencies. However, the required
45 Create a working branch
78 dependencies for IPython should be only the Python standard library.
46 -----------------------
79 - Upon installation check to see which optional dependencies are present and tell
80 the user which parts of IPython need which optional dependencies.
81
47
82 It is absolutely critical that each subpackage of IPython has a clearly specified set
48 When working on IPython, you won't actually make edits directly to the
83 of dependencies and that dependencies are not carelessly inherited from other IPython
49 :file:`lp:ipython` branch. Instead, you will create a separate branch for your
84 subpackages. Furthermore, tests that have certain dependencies should not fail if
50 changes. For now, let's assume you want to do your work in a branch named
85 those dependencies are not present. Instead they should be skipped and print a
51 "ipython-mybranch". Create this branch by doing::
86 message.
87
52
88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
53 $ bzr branch ipython ipython-mybranch
89 .. _distutils: http://docs.python.org/lib/module-distutils.html
90 .. _Matplotlib: http://matplotlib.sourceforge.net/
91
54
92 Specific subpackages
55 When you actually create a branch, you will want to give it a name that
93 --------------------
56 reflects the nature of the work that you will be doing in it, like
57 "install-docs-update".
94
58
95 ``core``
59 Make edits in your working branch
96 This is the core functionality of IPython that is independent of the
60 ---------------------------------
97 terminal, network and GUIs. Most of the code that is in the current
98 IPython trunk will be refactored, cleaned up and moved here.
99
61
100 ``kernel``
62 Now you are ready to actually make edits in your :file:`ipython-mybranch`
101 The enables the IPython core to be expose to a the network. This is
63 branch. Before doing this, it is helpful to install this branch so you can
102 also where all of the parallel computing capabilities are to be found.
64 test your changes as you work. This is easiest if you have setuptools
103
65 installed. Then, just do::
104 ``config``
105 The configuration package used by IPython.
106
66
107 ``frontends``
67 $ cd ipython-mybranch
108 The various frontends for IPython. A frontend is the end-user application
68 $ python setupegg.py develop
109 that exposes the capabilities of IPython to the user. The most basic frontend
110 will simply be a terminal based application that looks just like today 's
111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
112
113 ``notebook``
114 An application that allows users to work with IPython notebooks.
115
116 ``tools``
117 This is where general utilities go.
118
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::
119
74
120 Version control
75 $ ...do work in ipython-mybranch...
121 ===============
76 $ bzr commit -m "the commit message goes here"
122
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 development. First, you should install Bazaar. After you have done that, make
126 sure that it is working by getting the latest main branch of IPython::
127
128 $ bzr branch lp:ipython
129
130 Now you can create a new branch for you to do your work in::
131
77
132 $ bzr branch ipython ipython-mybranch
78 Please note that since we now don't use an old-style linear ChangeLog (that
133
79 tends to cause problems with distributed version control systems), you should
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
80 ensure that your log messages are reasonably detailed. Use a docstring-like
135 and then commit those changes using the commit command::
81 approach in the commit messages (including the second line being left
136
82 *blank*)::
137 $ ...do work in ipython-mybranch...
138 $ bzr ci -m "the commit message goes here"
139
140 Please note that since we now don't use an old-style linear ChangeLog
141 (that tends to cause problems with distributed version control
142 systems), you should ensure that your log messages are reasonably
143 detailed. Use a docstring-like approach in the commit messages
144 (including the second line being left *blank*)::
145
83
146 Single line summary of changes being committed.
84 Single line summary of changes being committed.
147
85
148 - more details when warranted ...
86 * more details when warranted ...
149 - including crediting outside contributors if they sent the
87 * including crediting outside contributors if they sent the
150 code/bug/idea!
88 code/bug/idea!
151
89
152 If we couple this with a policy of making single commits for each
90 As you work, you will repeat this edit/commit cycle many times. If you work on
153 reasonably atomic change, the bzr log should give an excellent view of
91 your branch for a long time, you will also want to get the latest changes from
154 the project, and 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::
155
94
156 While working with this branch, it is a good idea to merge in changes that have been
95 $ ls
157 made upstream in the parent branch. This can be done by doing::
96 ipython
97 ipython-mybranch
98
99 $ cd ipython
100 $ bzr pull
101 $ cd ../ipython-mybranch
102 $ bzr merge ../ipython
103 $ bzr commit -m "Merging changes from trunk"
158
104
159 $ bzr pull
105 Along the way, you should also run the IPython test suite. You can do this using the :command:`iptest` command::
160
161 If this command shows that the branches have diverged, then you should do a merge
162 instead::
163
106
164 $ bzr merge lp:ipython
107 $ cd
108 $ iptest
165
109
166 If you want others to be able to see your branch, you can create an account with
110 The :command:`iptest` command will also pick up and run any tests you have written.
167 launchpad and push the branch to your own workspace::
168
111
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
112 Post your branch and request a code review
113 ------------------------------------------
170
114
171 Finally, once the work in your branch is done, you can merge your changes back into
115 Once you are done with your edits, you should post your branch on Launchpad so
172 the `ipython` branch by using merge::
116 that other IPython developers can review the changes and help you merge your
117 changes into the main development branch. To post your branch on Launchpad,
118 do::
173
119
174 $ cd ipython
120 $ cd ipython-mybranch
175 $ merge ../ipython-mybranch
121 $ bzr push lp:~yourusername/ipython/ipython-mybranch
176 [resolve any conflicts]
177 $ bzr ci -m "Fixing that bug"
178 $ bzr push
179
122
180 But this will require you to have write permissions to the `ipython` branch. It you don't
123 Then, go to the `IPython Launchpad site <www.launchpad.net/ipython>`_, and you
181 you can tell one of the IPython devs about your branch and they can do the merge for 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?
182
128
183 More information about Bazaar workflows can be found `here`__.
129 This let's the other IPython developers know that your branch is ready to be
130 reviewed and merged into the main development branch. During this review
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:
184
133
185 .. __: http://subversion.tigris.org/
134 * All code is documented.
186 .. __: http://bazaar-vcs.org/
135 * All code has tests.
187 .. __: http://www.launchpad.net/ipython
136 * The entire IPython test suite passes.
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
137
138 Once your changes have been reviewed and approved, someone will merge them
139 into the main development branch.
189
140
190 Documentation
141 Documentation
191 =============
142 =============
@@ -193,38 +144,32 b' Documentation'
193 Standalone documentation
144 Standalone documentation
194 ------------------------
145 ------------------------
195
146
196 All standalone documentation should be written in plain text (``.txt``) files using
147 All standalone documentation should be written in plain text (``.txt``) files
197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
148 using reStructuredText [reStructuredText]_ for markup and formatting. All such
198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
149 documentation should be placed in directory :file:`docs/source` of the IPython
199 a suitably named subdirectory should be used. The documentation in this location will
150 source tree. The documentation in this location will serve as the main source
200 serve as the main source for IPython documentation and all existing documentation
151 for IPython documentation and all existing documentation should be converted
201 should be converted to this format.
152 to this format.
202
153
203 In the future, the text files in the ``docs`` directory will be used to generate all
154 To build the final documentation, we use Sphinx [Sphinx]_. Once you have Sphinx installed, you can build the html docs yourself by doing::
204 forms of documentation for IPython. This include documentation on the IPython website
205 as well as *pdf* documentation.
206
155
207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
156 $ cd ipython-mybranch/docs
157 $ make html
208
158
209 Docstring format
159 Docstring format
210 ----------------
160 ----------------
211
161
212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
162 Good docstrings are very important. All new code should have docstrings that
213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
163 are formatted using reStructuredText for markup and formatting, since it is
214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
164 understood by a wide variety of tools. Details about using reStructuredText
215 variety of tools. This means that if in the future we have any reason to change from
165 for docstrings can be found `here
216 `Epydoc`_ to something else, we'll have fewer transition pains.
217
218 Details about using `reStructuredText`_ for docstrings can be found `here
219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
166 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
220
167
221 .. _Epydoc: http://epydoc.sourceforge.net/
222
223 Additional PEPs of interest regarding documentation of code:
168 Additional PEPs of interest regarding documentation of code:
224
169
225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
170 * `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
226 - `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>`_
227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
172 * `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
228
173
229
174
230 Coding conventions
175 Coding conventions
@@ -233,128 +178,133 b' Coding conventions'
233 General
178 General
234 -------
179 -------
235
180
236 In general, we'll try to follow the standard Python style conventions as described here:
181 In general, we'll try to follow the standard Python style conventions as
182 described here:
237
183
238 - `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>`_
239
185
240
186
241 Other comments:
187 Other comments:
242
188
243 - In a large file, top level classes and functions should be
189 * In a large file, top level classes and functions should be
244 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.
245 - Use 4 spaces for indentation.
191 * Use 4 spaces for indentation.
246 - 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
247 methods. This is particularly true for classes that implement
193 methods. This is particularly true for classes that implement an interface.
248 similar interfaces and for interfaces that are similar.
249
194
250 Naming conventions
195 Naming conventions
251 ------------------
196 ------------------
252
197
253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
198 In terms of naming conventions, we'll follow the guidelines from the `Style
254 Python Code`_.
199 Guide for Python Code`_.
255
200
256 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:
257
202
258 - All ``lowercase`` module names.
203 * All ``lowercase`` module names.
259
204
260 - ``CamelCase`` for class names.
205 * ``CamelCase`` for class names.
261
206
262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
207 * ``lowercase_with_underscores`` for methods, functions, variables and
208 attributes.
263
209
264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
210 There are, however, some important exceptions to these rules. In some cases,
265 convention, providing shadow names for backward compatibility in public interfaces.
211 IPython code will interface with packages (Twisted, Wx, Qt) that use other
212 conventions. At some level this makes it impossible to adhere to our own
213 standards at all times. In particular, when subclassing classes that use other
214 naming conventions, you must follow their naming conventions. To deal with
215 cases like this, we propose the following policy:
266
216
267 There are, however, some important exceptions to these rules. In some cases, IPython
217 * If you are subclassing a class that uses different conventions, use its
268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
218 naming conventions throughout your subclass. Thus, if you are creating a
219 Twisted Protocol class, used Twisted's
220 ``namingSchemeForMethodsAndAttributes.``
269
221
270 - If you are subclassing a class that uses different conventions, use its
222 * All IPython's official interfaces should use our conventions. In some cases
271 naming conventions throughout your subclass. Thus, if you are creating a
223 this will mean that you need to provide shadow names (first implement
272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
224 ``fooBar`` and then ``foo_bar = fooBar``). We want to avoid this at all
225 costs, but it will probably be necessary at times. But, please use this
226 sparingly!
273
227
274 - All IPython's official interfaces should use our conventions. In some cases
228 Implementation-specific *private* methods will use
275 this will mean that you need to provide shadow names (first implement ``fooBar``
229 ``_single_underscore_prefix``. Names with a leading double underscore will
276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
230 *only* be used in special cases, as they makes subclassing difficult (such
277 will probably be necessary at times. But, please use this sparingly!
231 names are not easily seen by child classes).
278
232
279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
233 Occasionally some run-in lowercase names are used, but mostly for very short
280 Names with a leading double underscore will *only* be used in special cases, as they
234 names or where we are implementing methods very similar to existing ones in a
281 makes subclassing difficult (such names are not easily seen by child classes).
235 base class (like ``runlines()`` where ``runsource()`` and ``runcode()`` had
282
236 established precedent).
283 Occasionally some run-in lowercase names are used, but mostly for very short names or
284 where we are implementing methods very similar to existing ones in a base class (like
285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
286
237
287 The old IPython codebase has a big mix of classes and modules prefixed with an
238 The old IPython codebase has a big mix of classes and modules prefixed with an
288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
239 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned
289 namespaces offer cleaner prefixing. The only case where this approach is justified is
240 upon, as namespaces offer cleaner prefixing. The only case where this approach
290 for classes which are expected to be imported into external namespaces and a very
241 is justified is for classes which are expected to be imported into external
291 generic name (like Shell) is too likely to clash with something else. We'll need to
242 namespaces and a very generic name (like Shell) is too likely to clash with
292 revisit this issue as we clean up and refactor the code, but in general we should
243 something else. We'll need to revisit this issue as we clean up and refactor
293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
244 the code, but in general we should remove as many unnecessary ``IP``/``ip``
294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
245 prefixes as possible. However, if a prefix seems absolutely necessary the more
246 specific ``IPY`` or ``ipy`` are preferred.
295
247
296 .. _devel_testing:
248 .. _devel_testing:
297
249
298 Testing system
250 Testing system
299 ==============
251 ==============
300
252
301 It is extremely important that all code contributed to IPython has tests. Tests should
253 It is extremely important that all code contributed to IPython has tests.
302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
254 Tests should be written as unittests, doctests or as entities that the Nose
303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
255 [Nose]_ testing package will find. Regardless of how the tests are written, we
304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
256 will use Nose for discovering and running the tests. Nose will be required to
305 not be required to simply use IPython.
257 run the IPython test suite, but will not be required to simply use IPython.
306
307 .. _Nose: http://code.google.com/p/python-nose/
308
258
309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
259 Tests of Twisted using code need to follow two additional guidelines:
310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
311 run the tests and the twisted reactor will be handled correctly.
312
260
313 .. __: http://www.twistedmatrix.com
261 1. Twisted using tests should be written by subclassing the :class:`TestCase`
262 class that comes with :mod:`twisted.trial.unittest`.
314
263
315 Each subpackage in IPython should have its own ``tests`` directory that contains all
264 2. All :class:`Deferred` instances that are created in the test must be
316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
265 properly chained and the final one *must* be the return value of the test
317 a subpackage has any dependencies beyond the Python standard library, the tests for
266 method.
318 that subpackage should be skipped if the dependencies are not found. This is very
319 important so users don't get tests failing simply because they don't have dependencies.
320
267
321 We also need to look into use Noses ability to tag tests to allow a more modular
268 When these two things are done, Nose will be able to run the tests and the
322 approach of running tests.
269 twisted reactor will be handled correctly.
323
324 .. _devel_config:
325
270
326 Configuration system
271 Each subpackage in IPython should have its own :file:`tests` directory that
327 ====================
272 contains all of the tests for that subpackage. This allows each subpackage to
273 be self-contained. If a subpackage has any dependencies beyond the Python
274 standard library, the tests for that subpackage should be skipped if the
275 dependencies are not found. This is very important so users don't get tests
276 failing simply because they don't have dependencies.
328
277
329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
278 To run the IPython test suite, use the :command:`iptest` command that is installed with IPython::
330 improvement over the configuration system used in IPython. IPython works with these
331 files using the `ConfigObj`_ package, which IPython includes as
332 ``ipython1/external/configobj.py``.
333
279
334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
280 $ iptest
335 should contain a ``config`` subdirectory that contains all of the configuration
336 information for the subpackage. To see how configuration information is defined (along
337 with defaults) see at the examples in ``ipython1/kernel/config`` and
338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
340
341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
343 We won't actually use `Traits`_, but will implement something similar in pure Python.
344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
345 underneath the hood. Talk to Fernando if you are interested in working on this part of
346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
347
348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
350 .. _Traits: http://code.enthought.com/traits/
351
281
282 This command runs Nose with the proper options and extensions.
352
283
284 .. _devel_config:
353
285
286 Release checklist
287 =================
354
288
289 Most of the release process is automated by the :file:`release` script in the
290 :file:`tools` directory. This is just a handy reminder for the release manager.
355
291
292 #. Run the release script, which makes the tar.gz, eggs and Win32 .exe
293 installer. It posts them to the site and registers the release with PyPI.
356
294
295 #. Updating the website with announcements and links to the updated
296 changes.txt in html form. Remember to put a short note both on the news
297 page of the site and on Launcphad.
357
298
299 #. Drafting a short release announcement with i) highlights and ii) a link to
300 the html changes.txt.
358
301
302 #. Make sure that the released version of the docs is live on the site.
359
303
304 #. Celebrate!
360
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/
@@ -7,3 +7,5 b' Development'
7
7
8 development.txt
8 development.txt
9 roadmap.txt
9 roadmap.txt
10 notification_blueprint.txt
11 config_blueprint.txt
@@ -1,4 +1,4 b''
1 .. Notification:
1 .. _notification:
2
2
3 ==========================================
3 ==========================================
4 IPython.kernel.core.notification blueprint
4 IPython.kernel.core.notification blueprint
@@ -6,42 +6,78 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.
21
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.
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
27
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).
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::
33 from IPython.kernel.core.notification import NotificationCenter
56
34 center = NotificationCenter.sharedNotificationCenter
57 from IPython.kernel.core.notification import NotificationCenter
35 center.registerObserver(self, type=IPython.kernel.core.Interpreter.STDOUT_NOTIFICATION_TYPE, notifying_object=self.interpreter, callback=self.stdout_notification)
58 center = NotificationCenter.sharedNotificationCenter
36
59 center.registerObserver(self, type=IPython.kernel.core.Interpreter.STDOUT_NOTIFICATION_TYPE, notifying_object=self.interpreter, callback=self.stdout_notification)
37 and elsewhere in his front end::
60
38 def stdout_notification(self, type, notifying_object, out_string=None):
61 and elsewhere in his front end::
39 self.writeStdOut(out_string)
62
40
63 def stdout_notification(self, type, notifying_object, out_string=None):
41 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.
64 self.writeStdOut(out_string)
42
65
43 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...
66 If everything works, the Interpreter will (according to its published API)
44
67 fire a notification via the
45 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.
68 :data:`IPython.kernel.core.notification.sharedCenter` of type
46
69 :const:`STD_OUT_NOTIFICATION_TYPE` before writing anything to stdout [it's up
47 3. Barry is wrting a frontend for the IPython project. Because Barry's front end is the *new hotness*, it uses an asynchronous event model to communicate with a Twisted :mod:`~IPython.kernel.engineservice` that communicates with the IPython :class:`~IPython.kernel.core.interpreter.Interpreter`. Using the :mod:`IPython.kernel.notification` module, an asynchronous wrapper on the :mod:`IPython.kernel.core.notification` module, Barry's frontend can register for notifications from the interpreter that are delivered asynchronously. Even if Barry's frontend is running on a separate process or even host from the Interpreter, the notifications are delivered, as if by dark and twisted magic. Just like Dwight's frontend, Barry's frontend can now recieve notifications of e.g. writing to stdout/stderr, opening/closing an external file, an exception in the executing code, etc. No newline at end of file
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.
76
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...
80
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.
82
83 3. Barry is wrting a frontend for the IPython project. Because Barry's front end is the *new hotness*, it uses an asynchronous event model to communicate with a Twisted :mod:`~IPython.kernel.engineservice` that communicates with the IPython :class:`~IPython.kernel.core.interpreter.Interpreter`. Using the :mod:`IPython.kernel.notification` module, an asynchronous wrapper on the :mod:`IPython.kernel.core.notification` module, Barry's frontend can register for notifications from the interpreter that are delivered asynchronously. Even if Barry's frontend is running on a separate process or even host from the Interpreter, the notifications are delivered, as if by dark and twisted magic. Just like Dwight's frontend, Barry's frontend can now recieve notifications of e.g. writing to stdout/stderr, opening/closing an external file, an exception in the executing code, etc. No newline at end of file
@@ -4,93 +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
11
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.
12 Release 0.10
13 ------------
17
14
18 Steps along the way
15 * Initial refactor of :command:`ipcluster`.
19 ===================
20
16
21 Here we describe the various things that we need to work on to accomplish this goal.
17 * Better TextMate integration.
22
18
23 Setting up for regular release schedule
19 * Merge in the daemon branch.
24 ---------------------------------------
25
20
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.
21 Release 0.11
22 ------------
27
23
28 Process startup and management
24 * Refactor the configuration system and command line options for
29 ------------------------------
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.
30
28
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.
29 * Refactor :command:`ipcluster` to support the new configuration system.
32
30
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:
31 * Refactor the daemon stuff to support the new configuration system.
34
32
35 * It should possible to do everything through an extremely simple API that users
33 * Merge back in the core of the notebook.
36 can call from their own Python script. No shell commands should be needed.
37 * This simple API should be configured using standard .ini files.
38 * The system should make it possible to start processes using a number of different
39 approaches: SSH, PBS/Torque, Xgrid, Windows Server, mpirun, etc.
40 * The controller and engine processes should each have a daemon for monitoring,
41 signaling and clean up.
42 * The system should be secure.
43 * The system should work under all the major operating systems, including
44 Windows.
45
34
46 Initial work has begun on the daemon infrastructure, and some of the needed logic is contained in the ipcluster script.
35 Release 0.12
36 ------------
47
37
48 Ease of use/high-level approaches to parallelism
38 * Fully integrate process startup with the daemons for full process
49 ------------------------------------------------
39 management.
50
40
51 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.
41 * Make the capabilites of :command:`ipcluster` available from simple Python
42 classes.
52
43
53 We also need to think about new models for expressing parallelism. This is fun work as most of the foundation has already been established.
44 Major areas of work
45 ===================
54
46
55 Security
47 Refactoring the main IPython core
56 --------
48 ---------------------------------
57
49
58 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:
50 Process management for :mod:`IPython.kernel`
51 --------------------------------------------
59
52
60 * User authentication between all processes (engines, controller and clients).
53 Configuration system
61 * Optional TSL/SSL based encryption of all communication channels.
54 --------------------
62 * A good way of picking network ports so multiple users on the same system can
63 run their own controller and engines without interfering with those of others.
64 * A clear model for security that enables users to evaluate the security risks
65 associated with using IPython in various manners.
66
55
67 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.
56 Performance problems
57 --------------------
68
58
69 .. _Foolscap: http://foolscap.lothar.com/trac
59 Currently, we have a number of performance issues that are waiting to bite users:
70
60
71 The security work needs to be done in conjunction with other network protocol stuff.
61 * The controller stores a large amount of state in Python dictionaries. Under
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.
72
66
73 Latent performance issues
67 * We currently don't have a good way of handling large objects in the
74 -------------------------
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.
75
72
76 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.
77
75
78 * 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
79 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
80 develop more scalable solutions to this problem, such as using a sqlite database
78 controller and one each for the controller interfaces.
81 to store this state. This will also help the controller to be more fault tolerant.
82 * Currently, the client to controller connections are done through XML-RPC using
83 HTTP 1.0. This is very inefficient as XML-RPC is a very verbose protocol and
84 each request must be handled with a new connection. We need to move these network
85 connections over to PB or Foolscap.
86 * We currently don't have a good way of handling large objects in the controller.
87 The biggest problem is that because we don't have any way of streaming objects,
88 we get lots of temporary copies in the low-level buffers. We need to implement
89 a better serialization approach and true streaming support.
90 * The controller currently unpickles and repickles objects. We need to use the
91 [push|pull]_serialized methods instead.
92 * Currently the controller is a bottleneck. We need the ability to scale the
93 controller by aggregating multiple controllers into one effective controller.
94
79
95
80
96
81
@@ -16,10 +16,13 b' Will IPython speed my Python code up?'
16 Yes and no. When converting a serial code to run in parallel, there often many
16 Yes and no. When converting a serial code to run in parallel, there often many
17 difficulty questions that need to be answered, such as:
17 difficulty questions that need to be answered, such as:
18
18
19 * How should data be decomposed onto the set of processors?
19 * How should data be decomposed onto the set of processors?
20 * What are the data movement patterns?
20
21 * Can the algorithm be structured to minimize data movement?
21 * What are the data movement patterns?
22 * Is dynamic load balancing important?
22
23 * Can the algorithm be structured to minimize data movement?
24
25 * Is dynamic load balancing important?
23
26
24 We can't answer such questions for you. This is the hard (but fun) work of parallel
27 We can't answer such questions for you. This is the hard (but fun) work of parallel
25 computing. But, once you understand these things IPython will make it easier for you to
28 computing. But, once you understand these things IPython will make it easier for you to
@@ -28,9 +31,7 b' resulting parallel code interactively.'
28
31
29 With that said, if your problem is trivial to parallelize, IPython has a number of
32 With that said, if your problem is trivial to parallelize, IPython has a number of
30 different interfaces that will enable you to parallelize things is almost no time at
33 different interfaces that will enable you to parallelize things is almost no time at
31 all. A good place to start is the ``map`` method of our `multiengine interface`_.
34 all. A good place to start is the ``map`` method of our :class:`MultiEngineClient`.
32
33 .. _multiengine interface: ./parallel_multiengine
34
35
35 What is the best way to use MPI from Python?
36 What is the best way to use MPI from Python?
36 --------------------------------------------
37 --------------------------------------------
@@ -40,26 +41,33 b' What about all the other parallel computing packages in Python?'
40
41
41 Some of the unique characteristic of IPython are:
42 Some of the unique characteristic of IPython are:
42
43
43 * IPython is the only architecture that abstracts out the notion of a
44 * IPython is the only architecture that abstracts out the notion of a
44 parallel computation in such a way that new models of parallel computing
45 parallel computation in such a way that new models of parallel computing
45 can be explored quickly and easily. If you don't like the models we
46 can be explored quickly and easily. If you don't like the models we
46 provide, you can simply create your own using the capabilities we provide.
47 provide, you can simply create your own using the capabilities we provide.
47 * IPython is asynchronous from the ground up (we use `Twisted`_).
48
48 * IPython's architecture is designed to avoid subtle problems
49 * IPython is asynchronous from the ground up (we use `Twisted`_).
49 that emerge because of Python's global interpreter lock (GIL).
50
50 * While IPython'1 architecture is designed to support a wide range
51 * IPython's architecture is designed to avoid subtle problems
51 of novel parallel computing models, it is fully interoperable with
52 that emerge because of Python's global interpreter lock (GIL).
52 traditional MPI applications.
53
53 * IPython has been used and tested extensively on modern supercomputers.
54 * While IPython's architecture is designed to support a wide range
54 * IPython's networking layers are completely modular. Thus, is
55 of novel parallel computing models, it is fully interoperable with
55 straightforward to replace our existing network protocols with
56 traditional MPI applications.
56 high performance alternatives (ones based upon Myranet/Infiniband).
57
57 * IPython is designed from the ground up to support collaborative
58 * IPython has been used and tested extensively on modern supercomputers.
58 parallel computing. This enables multiple users to actively develop
59
59 and run the *same* parallel computation.
60 * IPython's networking layers are completely modular. Thus, is
60 * Interactivity is a central goal for us. While IPython does not have
61 straightforward to replace our existing network protocols with
61 to be used interactivly, is can be.
62 high performance alternatives (ones based upon Myranet/Infiniband).
62
63
64 * IPython is designed from the ground up to support collaborative
65 parallel computing. This enables multiple users to actively develop
66 and run the *same* parallel computation.
67
68 * Interactivity is a central goal for us. While IPython does not have
69 to be used interactivly, it can be.
70
63 .. _Twisted: http://www.twistedmatrix.com
71 .. _Twisted: http://www.twistedmatrix.com
64
72
65 Why The IPython controller a bottleneck in my parallel calculation?
73 Why The IPython controller a bottleneck in my parallel calculation?
@@ -71,13 +79,17 b' too much data is being pushed and pulled to and from the engines. If your algori'
71 is structured in this way, you really should think about alternative ways of
79 is structured in this way, you really should think about alternative ways of
72 handling the data movement. Here are some ideas:
80 handling the data movement. Here are some ideas:
73
81
74 1. Have the engines write data to files on the locals disks of the engines.
82 1. Have the engines write data to files on the locals disks of the engines.
75 2. Have the engines write data to files on a file system that is shared by
83
76 the engines.
84 2. Have the engines write data to files on a file system that is shared by
77 3. Have the engines write data to a database that is shared by the engines.
85 the engines.
78 4. Simply keep data in the persistent memory of the engines and move the
86
79 computation to the data (rather than the data to the computation).
87 3. Have the engines write data to a database that is shared by the engines.
80 5. See if you can pass data directly between engines using MPI.
88
89 4. Simply keep data in the persistent memory of the engines and move the
90 computation to the data (rather than the data to the computation).
91
92 5. See if you can pass data directly between engines using MPI.
81
93
82 Isn't Python slow to be used for high-performance parallel computing?
94 Isn't Python slow to be used for high-performance parallel computing?
83 ---------------------------------------------------------------------
95 ---------------------------------------------------------------------
@@ -7,50 +7,32 b' History'
7 Origins
7 Origins
8 =======
8 =======
9
9
10 The current IPython system grew out of the following three projects:
10 IPython was starting in 2001 by Fernando Perez. IPython as we know it
11
11 today grew out of the following three projects:
12 * [ipython] by Fernando Pérez. I was working on adding
12
13 Mathematica-type prompts and a flexible configuration system
13 * ipython by Fernando Pérez. I was working on adding
14 (something better than $PYTHONSTARTUP) to the standard Python
14 Mathematica-type prompts and a flexible configuration system
15 interactive interpreter.
15 (something better than $PYTHONSTARTUP) to the standard Python
16 * [IPP] by Janko Hauser. Very well organized, great usability. Had
16 interactive interpreter.
17 an old help system. IPP was used as the 'container' code into
17 * IPP by Janko Hauser. Very well organized, great usability. Had
18 which I added the functionality from ipython and LazyPython.
18 an old help system. IPP was used as the 'container' code into
19 * [LazyPython] by Nathan Gray. Simple but very powerful. The quick
19 which I added the functionality from ipython and LazyPython.
20 syntax (auto parens, auto quotes) and verbose/colored tracebacks
20 * LazyPython by Nathan Gray. Simple but very powerful. The quick
21 were all taken from here.
21 syntax (auto parens, auto quotes) and verbose/colored tracebacks
22
22 were all taken from here.
23 When I found out about IPP and LazyPython I tried to join all three
23
24 into a unified system. I thought this could provide a very nice
24 Here is how Fernando describes it:
25 working environment, both for regular programming and scientific
25
26 computing: shell-like features, IDL/Matlab numerics, Mathematica-type
26 When I found out about IPP and LazyPython I tried to join all three
27 prompt history and great object introspection and help facilities. I
27 into a unified system. I thought this could provide a very nice
28 think it worked reasonably well, though it was a lot more work than I
28 working environment, both for regular programming and scientific
29 had initially planned.
29 computing: shell-like features, IDL/Matlab numerics, Mathematica-type
30
30 prompt history and great object introspection and help facilities. I
31
31 think it worked reasonably well, though it was a lot more work than I
32 Current status
32 had initially planned.
33 ==============
33
34
34 Today and how we got here
35 The above listed features work, and quite well for the most part. But
35 =========================
36 until a major internal restructuring is done (see below), only bug
36
37 fixing will be done, no other features will be added (unless very minor
37 This needs to be filled in.
38 and well localized in the cleaner parts of the code).
38
39
40 IPython consists of some 18000 lines of pure python code, of which
41 roughly two thirds is reasonably clean. The rest is, messy code which
42 needs a massive restructuring before any further major work is done.
43 Even the messy code is fairly well documented though, and most of the
44 problems in the (non-existent) class design are well pointed to by a
45 PyChecker run. So the rewriting work isn't that bad, it will just be
46 time-consuming.
47
48
49 Future
50 ------
51
52 See the separate new_design document for details. Ultimately, I would
53 like to see IPython become part of the standard Python distribution as a
54 'big brother with batteries' to the standard Python interactive
55 interpreter. But that will never happen with the current state of the
56 code, so all contributions are welcome. No newline at end of file
@@ -2,11 +2,15 b''
2 IPython Documentation
2 IPython Documentation
3 =====================
3 =====================
4
4
5 Contents
5 .. htmlonly::
6 ========
6
7 :Release: |release|
8 :Date: |today|
9
10 Contents:
7
11
8 .. toctree::
12 .. toctree::
9 :maxdepth: 1
13 :maxdepth: 2
10
14
11 overview.txt
15 overview.txt
12 install/index.txt
16 install/index.txt
@@ -20,9 +24,7 b' Contents'
20 license_and_copyright.txt
24 license_and_copyright.txt
21 credits.txt
25 credits.txt
22
26
23 Indices and tables
27 .. htmlonly::
24 ==================
28 * :ref:`genindex`
25
29 * :ref:`modindex`
26 * :ref:`genindex`
30 * :ref:`search`
27 * :ref:`modindex`
28 * :ref:`search` No newline at end of file
@@ -7,5 +7,4 b' Installation'
7 .. toctree::
7 .. toctree::
8 :maxdepth: 2
8 :maxdepth: 2
9
9
10 basic.txt
10 install.txt
11 advanced.txt
@@ -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,14 +1,8 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::
5 .. _command_line_options:
10
11 .. _Command line options:
12
6
13 Command-line usage
7 Command-line usage
14 ==================
8 ==================
@@ -288,12 +282,13 b' All options with a [no] prepended can be specified in negated form'
288 recursive inclusions.
282 recursive inclusions.
289
283
290 -prompt_in1, pi1 <string>
284 -prompt_in1, pi1 <string>
291 Specify the string used for input prompts. Note that if you
285
292 are using numbered prompts, the number is represented with a
286 Specify the string used for input prompts. Note that if you are using
293 '\#' in the string. Don't forget to quote strings with spaces
287 numbered prompts, the number is represented with a '\#' in the
294 embedded in them. Default: 'In [\#]:'. Sec. Prompts_
288 string. Don't forget to quote strings with spaces embedded in
295 discusses in detail all the available escapes to customize
289 them. Default: 'In [\#]:'. The :ref:`prompts section <prompts>`
296 your prompts.
290 discusses in detail all the available escapes to customize your
291 prompts.
297
292
298 -prompt_in2, pi2 <string>
293 -prompt_in2, pi2 <string>
299 Similar to the previous option, but used for the continuation
294 Similar to the previous option, but used for the continuation
@@ -2077,13 +2072,14 b' customizations.'
2077 Access to the standard Python help
2072 Access to the standard Python help
2078 ----------------------------------
2073 ----------------------------------
2079
2074
2080 As of Python 2.1, a help system is available with access to object
2075 As of Python 2.1, a help system is available with access to object docstrings
2081 docstrings and the Python manuals. Simply type 'help' (no quotes) to
2076 and the Python manuals. Simply type 'help' (no quotes) to access it. You can
2082 access it. You can also type help(object) to obtain information about a
2077 also type help(object) to obtain information about a given object, and
2083 given object, and help('keyword') for information on a keyword. As noted
2078 help('keyword') for information on a keyword. As noted :ref:`here
2084 in sec. `accessing help`_, you need to properly configure
2079 <accessing_help>`, you need to properly configure your environment variable
2085 your environment variable PYTHONDOCS for this feature to work correctly.
2080 PYTHONDOCS for this feature to work correctly.
2086
2081
2082 .. _dynamic_object_info:
2087
2083
2088 Dynamic object information
2084 Dynamic object information
2089 --------------------------
2085 --------------------------
@@ -2126,7 +2122,7 b' are not really defined as separate identifiers. Try for example typing'
2126 {}.get? or after doing import os, type os.path.abspath??.
2122 {}.get? or after doing import os, type os.path.abspath??.
2127
2123
2128
2124
2129 .. _Readline:
2125 .. _readline:
2130
2126
2131 Readline-based features
2127 Readline-based features
2132 -----------------------
2128 -----------------------
@@ -2240,10 +2236,9 b' explanation in your ipythonrc file.'
2240 Session logging and restoring
2236 Session logging and restoring
2241 -----------------------------
2237 -----------------------------
2242
2238
2243 You can log all input from a session either by starting IPython with
2239 You can log all input from a session either by starting IPython with the
2244 the command line switches -log or -logfile (see sec. `command line
2240 command line switches -log or -logfile (see :ref:`here <command_line_options>`)
2245 options`_) or by activating the logging at any moment with the magic
2241 or by activating the logging at any moment with the magic function %logstart.
2246 function %logstart.
2247
2242
2248 Log files can later be reloaded with the -logplay option and IPython
2243 Log files can later be reloaded with the -logplay option and IPython
2249 will attempt to 'replay' the log by executing all the lines in it, thus
2244 will attempt to 'replay' the log by executing all the lines in it, thus
@@ -2279,6 +2274,8 b' resume logging to a file which had previously been started with'
2279 %logstart. They will fail (with an explanation) if you try to use them
2274 %logstart. They will fail (with an explanation) if you try to use them
2280 before logging has been started.
2275 before logging has been started.
2281
2276
2277 .. _system_shell_access:
2278
2282 System shell access
2279 System shell access
2283 -------------------
2280 -------------------
2284
2281
@@ -2389,14 +2386,16 b" These features are basically a terminal version of Ka-Ping Yee's cgitb"
2389 module, now part of the standard Python library.
2386 module, now part of the standard Python library.
2390
2387
2391
2388
2392 .. _Input caching:
2389 .. _input_caching:
2393
2390
2394 Input caching system
2391 Input caching system
2395 --------------------
2392 --------------------
2396
2393
2397 IPython offers numbered prompts (In/Out) with input and output caching.
2394 IPython offers numbered prompts (In/Out) with input and output caching
2398 All input is saved and can be retrieved as variables (besides the usual
2395 (also referred to as 'input history'). All input is saved and can be
2399 arrow key recall).
2396 retrieved as variables (besides the usual arrow key recall), in
2397 addition to the %rep magic command that brings a history entry
2398 up for editing on the next command line.
2400
2399
2401 The following GLOBAL variables always exist (so don't overwrite them!):
2400 The following GLOBAL variables always exist (so don't overwrite them!):
2402 _i: stores previous input. _ii: next previous. _iii: next-next previous.
2401 _i: stores previous input. _ii: next previous. _iii: next-next previous.
@@ -2429,7 +2428,43 b' sec. 6.2 <#sec:magic> for more details on the macro system.'
2429 A history function %hist allows you to see any part of your input
2428 A history function %hist allows you to see any part of your input
2430 history by printing a range of the _i variables.
2429 history by printing a range of the _i variables.
2431
2430
2432 .. _Output caching:
2431 You can also search ('grep') through your history by typing
2432 '%hist -g somestring'. This also searches through the so called *shadow history*,
2433 which remembers all the commands (apart from multiline code blocks)
2434 you have ever entered. Handy for searching for svn/bzr URL's, IP adrresses
2435 etc. You can bring shadow history entries listed by '%hist -g' up for editing
2436 (or re-execution by just pressing ENTER) with %rep command. Shadow history
2437 entries are not available as _iNUMBER variables, and they are identified by
2438 the '0' prefix in %hist -g output. That is, history entry 12 is a normal
2439 history entry, but 0231 is a shadow history entry.
2440
2441 Shadow history was added because the readline history is inherently very
2442 unsafe - if you have multiple IPython sessions open, the last session
2443 to close will overwrite the history of previountly closed session. Likewise,
2444 if a crash occurs, history is never saved, whereas shadow history entries
2445 are added after entering every command (so a command executed
2446 in another IPython session is immediately available in other IPython
2447 sessions that are open).
2448
2449 To conserve space, a command can exist in shadow history only once - it doesn't
2450 make sense to store a common line like "cd .." a thousand times. The idea is
2451 mainly to provide a reliable place where valuable, hard-to-remember commands can
2452 always be retrieved, as opposed to providing an exact sequence of commands
2453 you have entered in actual order.
2454
2455 Because shadow history has all the commands you have ever executed,
2456 time taken by %hist -g will increase oven time. If it ever starts to take
2457 too long (or it ends up containing sensitive information like passwords),
2458 clear the shadow history by `%clear shadow_nuke`.
2459
2460 Time taken to add entries to shadow history should be negligible, but
2461 in any case, if you start noticing performance degradation after using
2462 IPython for a long time (or running a script that floods the shadow history!),
2463 you can 'compress' the shadow history by executing
2464 `%clear shadow_compress`. In practice, this should never be necessary
2465 in normal use.
2466
2467 .. _output_caching:
2433
2468
2434 Output caching system
2469 Output caching system
2435 ---------------------
2470 ---------------------
@@ -2472,7 +2507,7 b' Directory history'
2472
2507
2473 Your history of visited directories is kept in the global list _dh, and
2508 Your history of visited directories is kept in the global list _dh, and
2474 the magic %cd command can be used to go to any entry in that list. The
2509 the magic %cd command can be used to go to any entry in that list. The
2475 %dhist command allows you to view this history. do ``cd -<TAB`` to
2510 %dhist command allows you to view this history. Do ``cd -<TAB`` to
2476 conventiently view the directory history.
2511 conventiently view the directory history.
2477
2512
2478
2513
@@ -3034,7 +3069,7 b' which is being shared by the interactive IPython loop and your GUI'
3034 thread, you should really handle it with thread locking and
3069 thread, you should really handle it with thread locking and
3035 syncrhonization properties. The Python documentation discusses these.
3070 syncrhonization properties. The Python documentation discusses these.
3036
3071
3037 .. _Interactive demos:
3072 .. _interactive_demos:
3038
3073
3039 Interactive demos with IPython
3074 Interactive demos with IPython
3040 ==============================
3075 ==============================
@@ -3143,21 +3178,17 b' toolkits, including Tk, GTK and WXPython. It also provides a number of'
3143 commands useful for scientific computing, all with a syntax compatible
3178 commands useful for scientific computing, all with a syntax compatible
3144 with that of the popular Matlab program.
3179 with that of the popular Matlab program.
3145
3180
3146 IPython accepts the special option -pylab (Sec. `Command line
3181 IPython accepts the special option -pylab (see :ref:`here
3147 options`_). This configures it to support matplotlib, honoring the
3182 <command_line_options>`). This configures it to support matplotlib, honoring
3148 settings in the .matplotlibrc file. IPython will detect the user's
3183 the settings in the .matplotlibrc file. IPython will detect the user's choice
3149 choice of matplotlib GUI backend, and automatically select the proper
3184 of matplotlib GUI backend, and automatically select the proper threading model
3150 threading model to prevent blocking. It also sets matplotlib in
3185 to prevent blocking. It also sets matplotlib in interactive mode and modifies
3151 interactive mode and modifies %run slightly, so that any
3186 %run slightly, so that any matplotlib-based script can be executed using %run
3152 matplotlib-based script can be executed using %run and the final
3187 and the final show() command does not block the interactive shell.
3153 show() command does not block the interactive shell.
3188
3154
3189 The -pylab option must be given first in order for IPython to configure its
3155 The -pylab option must be given first in order for IPython to
3190 threading mode. However, you can still issue other options afterwards. This
3156 configure its threading mode. However, you can still issue other
3191 allows you to have a matplotlib-based environment customized with additional
3157 options afterwards. This allows you to have a matplotlib-based
3192 modules using the standard IPython profile mechanism (see :ref:`here
3158 environment customized with additional modules using the standard
3193 <profiles>`): ``ipython -pylab -p myprofile`` will load the profile defined in
3159 IPython profile mechanism (Sec. Profiles_): ''ipython -pylab -p
3194 ipythonrc-myprofile after configuring matplotlib.
3160 myprofile'' will load the profile defined in ipythonrc-myprofile after
3161 configuring matplotlib.
3162
3163
@@ -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
@@ -24,11 +22,11 b' Tab completion'
24 --------------
22 --------------
25
23
26 TAB-completion, especially for attributes, is a convenient way to explore the
24 TAB-completion, especially for attributes, is a convenient way to explore the
27 structure of any object you're dealing with. Simply type object_name.<TAB>
25 structure of any object you're dealing with. Simply type object_name.<TAB> and
28 and a list of the object's attributes will be printed (see readline_ for
26 a list of the object's attributes will be printed (see :ref:`the readline
29 more). Tab completion also works on file and directory names, which combined
27 section <readline>` for more). Tab completion also works on file and directory
30 with IPython's alias system allows you to do from within IPython many of the
28 names, which combined with IPython's alias system allows you to do from within
31 things you normally would need the system shell for.
29 IPython many of the things you normally would need the system shell for.
32
30
33 Explore your objects
31 Explore your objects
34 --------------------
32 --------------------
@@ -39,18 +37,18 b' constructor details for classes. The magic commands %pdoc, %pdef, %psource'
39 and %pfile will respectively print the docstring, function definition line,
37 and %pfile will respectively print the docstring, function definition line,
40 full source code and the complete file for any object (when they can be
38 full source code and the complete file for any object (when they can be
41 found). If automagic is on (it is by default), you don't need to type the '%'
39 found). If automagic is on (it is by default), you don't need to type the '%'
42 explicitly. See sec. `dynamic object information`_ for more.
40 explicitly. See :ref:`this section <dynamic_object_info>` for more.
43
41
44 The `%run` magic command
42 The `%run` magic command
45 ------------------------
43 ------------------------
46
44
47 The %run magic command allows you to run any python script and load all of
45 The %run magic command allows you to run any python script and load all of its
48 its data directly into the interactive namespace. Since the file is re-read
46 data directly into the interactive namespace. Since the file is re-read from
49 from disk each time, changes you make to it are reflected immediately (in
47 disk each time, changes you make to it are reflected immediately (in contrast
50 contrast to the behavior of import). I rarely use import for code I am
48 to the behavior of import). I rarely use import for code I am testing, relying
51 testing, relying on %run instead. See magic_ section for more on this and
49 on %run instead. See :ref:`this section <magic>` for more on this and other
52 other magic commands, or type the name of any magic command and ? to get
50 magic commands, or type the name of any magic command and ? to get details on
53 details on it. See also sec. dreload_ for a recursive reload command. %run
51 it. See also :ref:`this section <dreload>` for a recursive reload command. %run
54 also has special flags for timing the execution of your scripts (-t) and for
52 also has special flags for timing the execution of your scripts (-t) and for
55 executing them under the control of either Python's pdb debugger (-d) or
53 executing them under the control of either Python's pdb debugger (-d) or
56 profiler (-p). With all of these, %run can be used as the main tool for
54 profiler (-p). With all of these, %run can be used as the main tool for
@@ -60,21 +58,21 b' choice.'
60 Debug a Python script
58 Debug a Python script
61 ---------------------
59 ---------------------
62
60
63 Use the Python debugger, pdb. The %pdb command allows you to toggle on and
61 Use the Python debugger, pdb. The %pdb command allows you to toggle on and off
64 off the automatic invocation of an IPython-enhanced pdb debugger (with
62 the automatic invocation of an IPython-enhanced pdb debugger (with coloring,
65 coloring, tab completion and more) at any uncaught exception. The advantage
63 tab completion and more) at any uncaught exception. The advantage of this is
66 of this is that pdb starts inside the function where the exception occurred,
64 that pdb starts inside the function where the exception occurred, with all data
67 with all data still available. You can print variables, see code, execute
65 still available. You can print variables, see code, execute statements and even
68 statements and even walk up and down the call stack to track down the true
66 walk up and down the call stack to track down the true source of the problem
69 source of the problem (which often is many layers in the stack above where
67 (which often is many layers in the stack above where the exception gets
70 the exception gets triggered). Running programs with %run and pdb active can
68 triggered). Running programs with %run and pdb active can be an efficient to
71 be an efficient to develop and debug code, in many cases eliminating the need
69 develop and debug code, in many cases eliminating the need for print statements
72 for print statements or external debugging tools. I often simply put a 1/0 in
70 or external debugging tools. I often simply put a 1/0 in a place where I want
73 a place where I want to take a look so that pdb gets called, quickly view
71 to take a look so that pdb gets called, quickly view whatever variables I need
74 whatever variables I need to or test various pieces of code and then remove
72 to or test various pieces of code and then remove the 1/0. Note also that '%run
75 the 1/0. Note also that '%run -d' activates pdb and automatically sets
73 -d' activates pdb and automatically sets initial breakpoints for you to step
76 initial breakpoints for you to step through your code, watch variables, etc.
74 through your code, watch variables, etc. The :ref:`output caching section
77 See Sec. `Output caching`_ for details.
75 <output_caching>` has more details.
78
76
79 Use the output cache
77 Use the output cache
80 --------------------
78 --------------------
@@ -84,7 +82,8 b' and variables named _1, _2, etc. alias them. For example, the result of input'
84 line 4 is available either as Out[4] or as _4. Additionally, three variables
82 line 4 is available either as Out[4] or as _4. Additionally, three variables
85 named _, __ and ___ are always kept updated with the for the last three
83 named _, __ and ___ are always kept updated with the for the last three
86 results. This allows you to recall any previous result and further use it for
84 results. This allows you to recall any previous result and further use it for
87 new calculations. See Sec. `Output caching`_ for more.
85 new calculations. See :ref:`the output caching section <output_caching>` for
86 more.
88
87
89 Suppress output
88 Suppress output
90 ---------------
89 ---------------
@@ -102,7 +101,7 b' A similar system exists for caching input. All input is stored in a global'
102 list called In , so you can re-execute lines 22 through 28 plus line 34 by
101 list called In , so you can re-execute lines 22 through 28 plus line 34 by
103 typing 'exec In[22:29]+In[34]' (using Python slicing notation). If you need
102 typing 'exec In[22:29]+In[34]' (using Python slicing notation). If you need
104 to execute the same set of lines often, you can assign them to a macro with
103 to execute the same set of lines often, you can assign them to a macro with
105 the %macro function. See sec. `Input caching`_ for more.
104 the %macro function. See :ref:`here <input_caching>` for more.
106
105
107 Use your input history
106 Use your input history
108 ----------------------
107 ----------------------
@@ -134,17 +133,18 b' into Python variables.'
134 Use Python variables when calling the shell
133 Use Python variables when calling the shell
135 -------------------------------------------
134 -------------------------------------------
136
135
137 Expand python variables when calling the shell (either via '!' and '!!' or
136 Expand python variables when calling the shell (either via '!' and '!!' or via
138 via aliases) by prepending a $ in front of them. You can also expand complete
137 aliases) by prepending a $ in front of them. You can also expand complete
139 python expressions. See `System shell access`_ for more.
138 python expressions. See :ref:`our shell section <system_shell_access>` for
139 more details.
140
140
141 Use profiles
141 Use profiles
142 ------------
142 ------------
143
143
144 Use profiles to maintain different configurations (modules to load, function
144 Use profiles to maintain different configurations (modules to load, function
145 definitions, option settings) for particular tasks. You can then have
145 definitions, option settings) for particular tasks. You can then have
146 customized versions of IPython for specific purposes. See sec. profiles_ for
146 customized versions of IPython for specific purposes. :ref:`This section
147 more.
147 <profiles>` has more details.
148
148
149
149
150 Embed IPython in your programs
150 Embed IPython in your programs
@@ -152,7 +152,7 b' Embed IPython in your programs'
152
152
153 A few lines of code are enough to load a complete IPython inside your own
153 A few lines of code are enough to load a complete IPython inside your own
154 programs, giving you the ability to work with your data interactively after
154 programs, giving you the ability to work with your data interactively after
155 automatic processing has been completed. See sec. embedding_ for more.
155 automatic processing has been completed. See :ref:`here <embedding>` for more.
156
156
157 Use the Python profiler
157 Use the Python profiler
158 -----------------------
158 -----------------------
@@ -166,8 +166,8 b' Use IPython to present interactive demos'
166 ----------------------------------------
166 ----------------------------------------
167
167
168 Use the IPython.demo.Demo class to load any Python script as an interactive
168 Use the IPython.demo.Demo class to load any Python script as an interactive
169 demo. With a minimal amount of simple markup, you can control the execution
169 demo. With a minimal amount of simple markup, you can control the execution of
170 of the script, stopping as needed. See sec. `interactive demos`_ for more.
170 the script, stopping as needed. See :ref:`here <interactive_demos>` for more.
171
171
172 Run doctests
172 Run doctests
173 ------------
173 ------------
@@ -1,61 +1,91 b''
1 .. _license:
1 .. _license:
2
2
3 =============================
3 =====================
4 License and Copyright
4 License and Copyright
5 =============================
5 =====================
6
6
7 This files needs to be updated to reflect what the new COPYING.txt files says about our license and copyright!
7 License
8 =======
8
9
9 IPython is released under the terms of the BSD license, whose general
10 IPython is licensed under the terms of the new or revised BSD license, as follows::
10 form can be found at: http://www.opensource.org/licenses/bsd-license.php. The full text of the
11 IPython license is reproduced below::
12
11
13 IPython is released under a BSD-type license.
12 Copyright (c) 2008, IPython Development Team
14
15 Copyright (c) 2001, 2002, 2003, 2004 Fernando Perez
16 <fperez@colorado.edu>.
17
18 Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and
19 Nathaniel Gray <n8gray@caltech.edu>.
20
13
21 All rights reserved.
14 All rights reserved.
22
15
23 Redistribution and use in source and binary forms, with or without
16 Redistribution and use in source and binary forms, with or without
24 modification, are permitted provided that the following conditions
17 modification, are permitted provided that the following conditions are
25 are met:
18 met:
26
19
27 a. Redistributions of source code must retain the above copyright
20 Redistributions of source code must retain the above copyright notice,
28 notice, this list of conditions and the following disclaimer.
21 this list of conditions and the following disclaimer.
29
22
30 b. Redistributions in binary form must reproduce the above copyright
23 Redistributions in binary form must reproduce the above copyright notice,
31 notice, this list of conditions and the following disclaimer in the
24 this list of conditions and the following disclaimer in the documentation
32 documentation and/or other materials provided with the distribution.
25 and/or other materials provided with the distribution.
33
26
34 c. Neither the name of the copyright holders nor the names of any
27 Neither the name of the IPython Development Team nor the names of its
35 contributors to this software may be used to endorse or promote
28 contributors may be used to endorse or promote products derived from this
36 products derived from this software without specific prior written
29 software without specific prior written permission.
37 permission.
30
38
31 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
39 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
40 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
41 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
34 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
35 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
43 REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
36 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
44 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
37 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
45 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
46 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
47 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
41 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42
50 POSSIBILITY OF SUCH DAMAGE.
43 About the IPython Development Team
51
44 ==================================
52 Individual authors are the holders of the copyright for their code and
45
53 are listed in each file.
46 Fernando Perez began IPython in 2001 based on code from Janko Hauser
47 <jhauser@zscout.de> and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still
48 the project lead.
49
50 The IPython Development Team is the set of all contributors to the IPython
51 project. This includes all of the IPython subprojects. Here is a list of the
52 currently active contributors:
53
54 * Matthieu Brucher
55 * Ondrej Certik
56 * Laurent Dufrechou
57 * Robert Kern
58 * Brian E. Granger
59 * Fernando Perez (project leader)
60 * Benjamin Ragan-Kelley
61 * Ville M. Vainio
62 * Gael Varoququx
63 * Stefan van der Walt
64 * Tech-X Corporation
65 * Barry Wark
66
67 If your name is missing, please add it.
68
69 Our Copyright Policy
70 ====================
71
72 IPython uses a shared copyright model. Each contributor maintains copyright
73 over their contributions to IPython. But, it is important to note that these
74 contributions are typically only changes to the repositories. Thus, the
75 IPython source code, in its entirety is not the copyright of any single person
76 or institution. Instead, it is the collective copyright of the entire IPython
77 Development Team. If individual contributors want to maintain a record of what
78 changes/contributions they have specific copyright on, they should indicate
79 their copyright in the commit message of the change, when they commit the
80 change to one of the IPython repositories.
81
82 Miscellaneous
83 =============
54
84
55 Some files (DPyGetOpt.py, for example) may be licensed under different
85 Some files (DPyGetOpt.py, for example) may be licensed under different
56 conditions. Ultimately each file indicates clearly the conditions under
86 conditions. Ultimately each file indicates clearly the conditions under which
57 which its author/authors have decided to publish the code.
87 its author/authors have decided to publish the code.
58
88
59 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
60 GNU Lesser General Public License (LGPL), available at
90 Lesser General Public License (LGPL), available at
61 http://www.gnu.org/copyleft/lesser.html. No newline at end of file
91 http://www.gnu.org/copyleft/lesser.html.
@@ -14,136 +14,164 b' However, the interpreter supplied with the standard Python distribution'
14 is somewhat limited for extended interactive use.
14 is somewhat limited for extended interactive use.
15
15
16 The goal of IPython is to create a comprehensive environment for
16 The goal of IPython is to create a comprehensive environment for
17 interactive and exploratory computing. To support, this goal, IPython
17 interactive and exploratory computing. To support this goal, IPython
18 has two main components:
18 has two main components:
19
19
20 * An enhanced interactive Python shell.
20 * An enhanced interactive Python shell.
21 * An architecture for interactive parallel computing.
21 * An architecture for interactive parallel computing.
22
22
23 All of IPython is open source (released under the revised BSD license).
23 All of IPython is open source (released under the revised BSD license).
24
24
25 Enhanced interactive Python shell
25 Enhanced interactive Python shell
26 =================================
26 =================================
27
27
28 IPython's interactive shell (`ipython`), has the following goals:
28 IPython's interactive shell (:command:`ipython`), has the following goals,
29
29 amongst others:
30 1. Provide an interactive shell superior to Python's default. IPython
30
31 has many features for object introspection, system shell access,
31 1. Provide an interactive shell superior to Python's default. IPython
32 and its own special command system for adding functionality when
32 has many features for object introspection, system shell access,
33 working interactively. It tries to be a very efficient environment
33 and its own special command system for adding functionality when
34 both for Python code development and for exploration of problems
34 working interactively. It tries to be a very efficient environment
35 using Python objects (in situations like data analysis).
35 both for Python code development and for exploration of problems
36 2. Serve as an embeddable, ready to use interpreter for your own
36 using Python objects (in situations like data analysis).
37 programs. IPython can be started with a single call from inside
37
38 another program, providing access to the current namespace. This
38 2. Serve as an embeddable, ready to use interpreter for your own
39 can be very useful both for debugging purposes and for situations
39 programs. IPython can be started with a single call from inside
40 where a blend of batch-processing and interactive exploration are
40 another program, providing access to the current namespace. This
41 needed.
41 can be very useful both for debugging purposes and for situations
42 3. Offer a flexible framework which can be used as the base
42 where a blend of batch-processing and interactive exploration are
43 environment for other systems with Python as the underlying
43 needed. New in the 0.9 version of IPython is a reusable wxPython
44 language. Specifically scientific environments like Mathematica,
44 based IPython widget.
45 IDL and Matlab inspired its design, but similar ideas can be
45
46 useful in many fields.
46 3. Offer a flexible framework which can be used as the base
47 4. Allow interactive testing of threaded graphical toolkits. IPython
47 environment for other systems with Python as the underlying
48 has support for interactive, non-blocking control of GTK, Qt and
48 language. Specifically scientific environments like Mathematica,
49 WX applications via special threading flags. The normal Python
49 IDL and Matlab inspired its design, but similar ideas can be
50 shell can only do this for Tkinter applications.
50 useful in many fields.
51
52 4. Allow interactive testing of threaded graphical toolkits. IPython
53 has support for interactive, non-blocking control of GTK, Qt and
54 WX applications via special threading flags. The normal Python
55 shell can only do this for Tkinter applications.
51
56
52 Main features of the interactive shell
57 Main features of the interactive shell
53 --------------------------------------
58 --------------------------------------
54
59
55 * Dynamic object introspection. One can access docstrings, function
60 * Dynamic object introspection. One can access docstrings, function
56 definition prototypes, source code, source files and other details
61 definition prototypes, source code, source files and other details
57 of any object accessible to the interpreter with a single
62 of any object accessible to the interpreter with a single
58 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
63 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
59 * Searching through modules and namespaces with :samp:`*` wildcards, both
64
60 when using the :samp:`?` system and via the :samp:`%psearch` command.
65 * Searching through modules and namespaces with :samp:`*` wildcards, both
61 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
66 when using the :samp:`?` system and via the :samp:`%psearch` command.
62 This works for keywords, modules, methods, variables and files in the
67
63 current directory. This is supported via the readline library, and
68 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
64 full access to configuring readline's behavior is provided.
69 This works for keywords, modules, methods, variables and files in the
65 Custom completers can be implemented easily for different purposes
70 current directory. This is supported via the readline library, and
66 (system commands, magic arguments etc.)
71 full access to configuring readline's behavior is provided.
67 * Numbered input/output prompts with command history (persistent
72 Custom completers can be implemented easily for different purposes
68 across sessions and tied to each profile), full searching in this
73 (system commands, magic arguments etc.)
69 history and caching of all input and output.
74
70 * User-extensible 'magic' commands. A set of commands prefixed with
75 * Numbered input/output prompts with command history (persistent
71 :samp:`%` is available for controlling IPython itself and provides
76 across sessions and tied to each profile), full searching in this
72 directory control, namespace information and many aliases to
77 history and caching of all input and output.
73 common system shell commands.
78
74 * Alias facility for defining your own system aliases.
79 * User-extensible 'magic' commands. A set of commands prefixed with
75 * Complete system shell access. Lines starting with :samp:`!` are passed
80 :samp:`%` is available for controlling IPython itself and provides
76 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
81 directory control, namespace information and many aliases to
77 captures shell output into python variables for further use.
82 common system shell commands.
78 * Background execution of Python commands in a separate thread.
83
79 IPython has an internal job manager called jobs, and a
84 * Alias facility for defining your own system aliases.
80 conveninence backgrounding magic function called :samp:`%bg`.
85
81 * The ability to expand python variables when calling the system
86 * Complete system shell access. Lines starting with :samp:`!` are passed
82 shell. In a shell command, any python variable prefixed with :samp:`$` is
87 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
83 expanded. A double :samp:`$$` allows passing a literal :samp:`$` to the shell (for
88 captures shell output into python variables for further use.
84 access to shell and environment variables like :envvar:`PATH`).
89
85 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
90 * Background execution of Python commands in a separate thread.
86 persistent bookmark system (using :samp:`%bookmark`) for fast access to
91 IPython has an internal job manager called jobs, and a
87 frequently visited directories.
92 convenience backgrounding magic function called :samp:`%bg`.
88 * A lightweight persistence framework via the :samp:`%store` command, which
93
89 allows you to save arbitrary Python variables. These get restored
94 * The ability to expand python variables when calling the system
90 automatically when your session restarts.
95 shell. In a shell command, any python variable prefixed with :samp:`$` is
91 * Automatic indentation (optional) of code as you type (through the
96 expanded. A double :samp:`$$` allows passing a literal :samp:`$` to the shell (for
92 readline library).
97 access to shell and environment variables like :envvar:`PATH`).
93 * Macro system for quickly re-executing multiple lines of previous
98
94 input with a single name. Macros can be stored persistently via
99 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
95 :samp:`%store` and edited via :samp:`%edit`.
100 persistent bookmark system (using :samp:`%bookmark`) for fast access to
96 * Session logging (you can then later use these logs as code in your
101 frequently visited directories.
97 programs). Logs can optionally timestamp all input, and also store
102
98 session output (marked as comments, so the log remains valid
103 * A lightweight persistence framework via the :samp:`%store` command, which
99 Python source code).
104 allows you to save arbitrary Python variables. These get restored
100 * Session restoring: logs can be replayed to restore a previous
105 automatically when your session restarts.
101 session to the state where you left it.
106
102 * Verbose and colored exception traceback printouts. Easier to parse
107 * Automatic indentation (optional) of code as you type (through the
103 visually, and in verbose mode they produce a lot of useful
108 readline library).
104 debugging information (basically a terminal version of the cgitb
109
105 module).
110 * Macro system for quickly re-executing multiple lines of previous
106 * Auto-parentheses: callable objects can be executed without
111 input with a single name. Macros can be stored persistently via
107 parentheses: :samp:`sin 3` is automatically converted to :samp:`sin(3)`.
112 :samp:`%store` and edited via :samp:`%edit`.
108 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
113
109 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
114 * Session logging (you can then later use these logs as code in your
110 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
115 programs). Logs can optionally timestamp all input, and also store
111 becomes :samp:`my_function("a b")`.
116 session output (marked as comments, so the log remains valid
112 * Extensible input syntax. You can define filters that pre-process
117 Python source code).
113 user input to simplify input in special situations. This allows
118
114 for example pasting multi-line code fragments which start with
119 * Session restoring: logs can be replayed to restore a previous
115 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
120 session to the state where you left it.
116 standard Python documentation.
121
117 * Flexible configuration system. It uses a configuration file which
122 * Verbose and colored exception traceback printouts. Easier to parse
118 allows permanent setting of all command-line options, module
123 visually, and in verbose mode they produce a lot of useful
119 loading, code and file execution. The system allows recursive file
124 debugging information (basically a terminal version of the cgitb
120 inclusion, so you can have a base file with defaults and layers
125 module).
121 which load other customizations for particular projects.
126
122 * Embeddable. You can call IPython as a python shell inside your own
127 * Auto-parentheses: callable objects can be executed without
123 python programs. This can be used both for debugging code or for
128 parentheses: :samp:`sin 3` is automatically converted to :samp:`sin(3)`.
124 providing interactive abilities to your programs with knowledge
129
125 about the local namespaces (very useful in debugging and data
130 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
126 analysis situations).
131 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
127 * Easy debugger access. You can set IPython to call up an enhanced
132 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
128 version of the Python debugger (pdb) every time there is an
133 becomes :samp:`my_function("a b")`.
129 uncaught exception. This drops you inside the code which triggered
134
130 the exception with all the data live and it is possible to
135 * Extensible input syntax. You can define filters that pre-process
131 navigate the stack to rapidly isolate the source of a bug. The
136 user input to simplify input in special situations. This allows
132 :samp:`%run` magic command (with the :samp:`-d` option) can run any script under
137 for example pasting multi-line code fragments which start with
133 pdb's control, automatically setting initial breakpoints for you.
138 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
134 This version of pdb has IPython-specific improvements, including
139 standard Python documentation.
135 tab-completion and traceback coloring support. For even easier
140
136 debugger access, try :samp:`%debug` after seeing an exception. winpdb is
141 * Flexible configuration system. It uses a configuration file which
137 also supported, see ipy_winpdb extension.
142 allows permanent setting of all command-line options, module
138 * Profiler support. You can run single statements (similar to
143 loading, code and file execution. The system allows recursive file
139 :samp:`profile.run()`) or complete programs under the profiler's control.
144 inclusion, so you can have a base file with defaults and layers
140 While this is possible with standard cProfile or profile modules,
145 which load other customizations for particular projects.
141 IPython wraps this functionality with magic commands (see :samp:`%prun`
146
142 and :samp:`%run -p`) convenient for rapid interactive work.
147 * Embeddable. You can call IPython as a python shell inside your own
143 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
148 python programs. This can be used both for debugging code or for
144 that allows you to paste existing doctests (with leading :samp:`>>>`
149 providing interactive abilities to your programs with knowledge
145 prompts and whitespace) and uses doctest-compatible prompts and
150 about the local namespaces (very useful in debugging and data
146 output, so you can use IPython sessions as doctest code.
151 analysis situations).
152
153 * Easy debugger access. You can set IPython to call up an enhanced
154 version of the Python debugger (pdb) every time there is an
155 uncaught exception. This drops you inside the code which triggered
156 the exception with all the data live and it is possible to
157 navigate the stack to rapidly isolate the source of a bug. The
158 :samp:`%run` magic command (with the :samp:`-d` option) can run any script under
159 pdb's control, automatically setting initial breakpoints for you.
160 This version of pdb has IPython-specific improvements, including
161 tab-completion and traceback coloring support. For even easier
162 debugger access, try :samp:`%debug` after seeing an exception. winpdb is
163 also supported, see ipy_winpdb extension.
164
165 * Profiler support. You can run single statements (similar to
166 :samp:`profile.run()`) or complete programs under the profiler's control.
167 While this is possible with standard cProfile or profile modules,
168 IPython wraps this functionality with magic commands (see :samp:`%prun`
169 and :samp:`%run -p`) convenient for rapid interactive work.
170
171 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
172 that allows you to paste existing doctests (with leading :samp:`>>>`
173 prompts and whitespace) and uses doctest-compatible prompts and
174 output, so you can use IPython sessions as doctest code.
147
175
148 Interactive parallel computing
176 Interactive parallel computing
149 ==============================
177 ==============================
@@ -153,6 +181,37 b' architecture within IPython that allows such hardware to be used quickly and eas'
153 from Python. Moreover, this architecture is designed to support interactive and
181 from Python. Moreover, this architecture is designed to support interactive and
154 collaborative parallel computing.
182 collaborative parallel computing.
155
183
184 The main features of this system are:
185
186 * Quickly parallelize Python code from an interactive Python/IPython session.
187
188 * A flexible and dynamic process model that be deployed on anything from
189 multicore workstations to supercomputers.
190
191 * An architecture that supports many different styles of parallelism, from
192 message passing to task farming. And all of these styles can be handled
193 interactively.
194
195 * Both blocking and fully asynchronous interfaces.
196
197 * High level APIs that enable many things to be parallelized in a few lines
198 of code.
199
200 * Write parallel code that will run unchanged on everything from multicore
201 workstations to supercomputers.
202
203 * Full integration with Message Passing libraries (MPI).
204
205 * Capabilities based security model with full encryption of network connections.
206
207 * Share live parallel jobs with other users securely. We call this collaborative
208 parallel computing.
209
210 * Dynamically load balanced task farming system.
211
212 * Robust error handling. Python exceptions raised in parallel execution are
213 gathered and presented to the top-level code.
214
156 For more information, see our :ref:`overview <parallel_index>` of using IPython for
215 For more information, see our :ref:`overview <parallel_index>` of using IPython for
157 parallel computing.
216 parallel computing.
158
217
@@ -1,17 +1,16 b''
1 .. _parallel_index:
1 .. _parallel_index:
2
2
3 ====================================
3 ====================================
4 Using IPython for Parallel computing
4 Using IPython for parallel computing
5 ====================================
5 ====================================
6
6
7 User Documentation
8 ==================
9
10 .. toctree::
7 .. toctree::
11 :maxdepth: 2
8 :maxdepth: 2
12
9
13 parallel_intro.txt
10 parallel_intro.txt
11 parallel_process.txt
14 parallel_multiengine.txt
12 parallel_multiengine.txt
15 parallel_task.txt
13 parallel_task.txt
16 parallel_mpi.txt
14 parallel_mpi.txt
15 parallel_security.txt
17
16
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from IPython/config/config.py to sandbox/config.py
NO CONTENT: file renamed from IPython/config/config.py to sandbox/config.py
1 NO CONTENT: file renamed from IPython/config/tests/sample_config.py to sandbox/sample_config.py
NO CONTENT: file renamed from IPython/config/tests/sample_config.py to sandbox/sample_config.py
1 NO CONTENT: file renamed from IPython/config/tests/test_config.py to sandbox/test_config.py
NO CONTENT: file renamed from IPython/config/tests/test_config.py to sandbox/test_config.py
1 NO CONTENT: file renamed from IPython/config/traitlets.py to sandbox/traitlets.py
NO CONTENT: file renamed from IPython/config/traitlets.py to sandbox/traitlets.py
1 NO CONTENT: modified file chmod 100644 => 100755, file renamed from scripts/wxIpython to scripts/ipython-wx
NO CONTENT: modified file chmod 100644 => 100755, file renamed from scripts/wxIpython to scripts/ipython-wx
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (660 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now