Show More
@@ -1,121 +1,121 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Release data for the IPython project.""" |
|
2 | """Release data for the IPython project.""" | |
3 |
|
3 | |||
4 | #***************************************************************************** |
|
4 | #***************************************************************************** | |
5 | # Copyright (C) 2008-2009 The IPython Development Team |
|
5 | # Copyright (C) 2008-2009 The IPython Development Team | |
6 | # Copyright (C) 2001-2008 Fernando Perez <fperez@colorado.edu> |
|
6 | # Copyright (C) 2001-2008 Fernando Perez <fperez@colorado.edu> | |
7 | # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray |
|
7 | # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray | |
8 | # <n8gray@caltech.edu> |
|
8 | # <n8gray@caltech.edu> | |
9 | # |
|
9 | # | |
10 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | # Distributed under the terms of the BSD License. The full license is in | |
11 | # the file COPYING, distributed as part of this software. |
|
11 | # the file COPYING, distributed as part of this software. | |
12 | #***************************************************************************** |
|
12 | #***************************************************************************** | |
13 |
|
13 | |||
14 | # Name of the package for release purposes. This is the name which labels |
|
14 | # Name of the package for release purposes. This is the name which labels | |
15 | # the tarballs and RPMs made by distutils, so it's best to lowercase it. |
|
15 | # the tarballs and RPMs made by distutils, so it's best to lowercase it. | |
16 | name = 'ipython' |
|
16 | name = 'ipython' | |
17 |
|
17 | |||
18 | # For versions with substrings (like 0.6.16.svn), use an extra . to separate |
|
18 | # For versions with substrings (like 0.6.16.svn), use an extra . to separate | |
19 | # the new substring. We have to avoid using either dashes or underscores, |
|
19 | # the new substring. We have to avoid using either dashes or underscores, | |
20 | # because bdist_rpm does not accept dashes (an RPM) convention, and |
|
20 | # because bdist_rpm does not accept dashes (an RPM) convention, and | |
21 | # bdist_deb does not accept underscores (a Debian convention). |
|
21 | # bdist_deb does not accept underscores (a Debian convention). | |
22 |
|
22 | |||
23 |
development = |
|
23 | development = True # change this to False to do a release | |
24 |
version_base = '0.1 |
|
24 | version_base = '0.11' | |
25 | branch = 'ipython' |
|
25 | branch = 'ipython' | |
26 |
revision = '1 |
|
26 | revision = '1205' | |
27 |
|
27 | |||
28 | if development: |
|
28 | if development: | |
29 | if branch == 'ipython': |
|
29 | if branch == 'ipython': | |
30 | version = '%s.bzr.r%s' % (version_base, revision) |
|
30 | version = '%s.bzr.r%s' % (version_base, revision) | |
31 | else: |
|
31 | else: | |
32 | version = '%s.bzr.r%s.%s' % (version_base, revision, branch) |
|
32 | version = '%s.bzr.r%s.%s' % (version_base, revision, branch) | |
33 | else: |
|
33 | else: | |
34 | version = version_base |
|
34 | version = version_base | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | description = "An interactive computing environment for Python" |
|
37 | description = "An interactive computing environment for Python" | |
38 |
|
38 | |||
39 | long_description = \ |
|
39 | long_description = \ | |
40 | """ |
|
40 | """ | |
41 | The goal of IPython is to create a comprehensive environment for |
|
41 | The goal of IPython is to create a comprehensive environment for | |
42 | interactive and exploratory computing. To support this goal, IPython |
|
42 | interactive and exploratory computing. To support this goal, IPython | |
43 | has two main components: |
|
43 | has two main components: | |
44 |
|
44 | |||
45 | * An enhanced interactive Python shell. |
|
45 | * An enhanced interactive Python shell. | |
46 |
|
46 | |||
47 | * An architecture for interactive parallel computing. |
|
47 | * An architecture for interactive parallel computing. | |
48 |
|
48 | |||
49 | The enhanced interactive Python shell has the following main features: |
|
49 | The enhanced interactive Python shell has the following main features: | |
50 |
|
50 | |||
51 | * Comprehensive object introspection. |
|
51 | * Comprehensive object introspection. | |
52 |
|
52 | |||
53 | * Input history, persistent across sessions. |
|
53 | * Input history, persistent across sessions. | |
54 |
|
54 | |||
55 | * Caching of output results during a session with automatically generated |
|
55 | * Caching of output results during a session with automatically generated | |
56 | references. |
|
56 | references. | |
57 |
|
57 | |||
58 | * Readline based name completion. |
|
58 | * Readline based name completion. | |
59 |
|
59 | |||
60 | * Extensible system of 'magic' commands for controlling the environment and |
|
60 | * Extensible system of 'magic' commands for controlling the environment and | |
61 | performing many tasks related either to IPython or the operating system. |
|
61 | performing many tasks related either to IPython or the operating system. | |
62 |
|
62 | |||
63 | * Configuration system with easy switching between different setups (simpler |
|
63 | * Configuration system with easy switching between different setups (simpler | |
64 | than changing $PYTHONSTARTUP environment variables every time). |
|
64 | than changing $PYTHONSTARTUP environment variables every time). | |
65 |
|
65 | |||
66 | * Session logging and reloading. |
|
66 | * Session logging and reloading. | |
67 |
|
67 | |||
68 | * Extensible syntax processing for special purpose situations. |
|
68 | * Extensible syntax processing for special purpose situations. | |
69 |
|
69 | |||
70 | * Access to the system shell with user-extensible alias system. |
|
70 | * Access to the system shell with user-extensible alias system. | |
71 |
|
71 | |||
72 | * Easily embeddable in other Python programs and wxPython GUIs. |
|
72 | * Easily embeddable in other Python programs and wxPython GUIs. | |
73 |
|
73 | |||
74 | * Integrated access to the pdb debugger and the Python profiler. |
|
74 | * Integrated access to the pdb debugger and the Python profiler. | |
75 |
|
75 | |||
76 | The parallel computing architecture has the following main features: |
|
76 | The parallel computing architecture has the following main features: | |
77 |
|
77 | |||
78 | * Quickly parallelize Python code from an interactive Python/IPython session. |
|
78 | * Quickly parallelize Python code from an interactive Python/IPython session. | |
79 |
|
79 | |||
80 | * A flexible and dynamic process model that be deployed on anything from |
|
80 | * A flexible and dynamic process model that be deployed on anything from | |
81 | multicore workstations to supercomputers. |
|
81 | multicore workstations to supercomputers. | |
82 |
|
82 | |||
83 | * An architecture that supports many different styles of parallelism, from |
|
83 | * An architecture that supports many different styles of parallelism, from | |
84 | message passing to task farming. |
|
84 | message passing to task farming. | |
85 |
|
85 | |||
86 | * Both blocking and fully asynchronous interfaces. |
|
86 | * Both blocking and fully asynchronous interfaces. | |
87 |
|
87 | |||
88 | * High level APIs that enable many things to be parallelized in a few lines |
|
88 | * High level APIs that enable many things to be parallelized in a few lines | |
89 | of code. |
|
89 | of code. | |
90 |
|
90 | |||
91 | * Share live parallel jobs with other users securely. |
|
91 | * Share live parallel jobs with other users securely. | |
92 |
|
92 | |||
93 | * Dynamically load balanced task farming system. |
|
93 | * Dynamically load balanced task farming system. | |
94 |
|
94 | |||
95 | * Robust error handling in parallel code. |
|
95 | * Robust error handling in parallel code. | |
96 |
|
96 | |||
97 | The latest development version is always available from IPython's `Launchpad |
|
97 | The latest development version is always available from IPython's `Launchpad | |
98 | site <http://launchpad.net/ipython>`_. |
|
98 | site <http://launchpad.net/ipython>`_. | |
99 | """ |
|
99 | """ | |
100 |
|
100 | |||
101 | license = 'BSD' |
|
101 | license = 'BSD' | |
102 |
|
102 | |||
103 | authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), |
|
103 | authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), | |
104 | 'Janko' : ('Janko Hauser','jhauser@zscout.de'), |
|
104 | 'Janko' : ('Janko Hauser','jhauser@zscout.de'), | |
105 | 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), |
|
105 | 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), | |
106 | 'Ville' : ('Ville Vainio','vivainio@gmail.com'), |
|
106 | 'Ville' : ('Ville Vainio','vivainio@gmail.com'), | |
107 | 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'), |
|
107 | 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'), | |
108 | 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com') |
|
108 | 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com') | |
109 | } |
|
109 | } | |
110 |
|
110 | |||
111 | author = 'The IPython Development Team' |
|
111 | author = 'The IPython Development Team' | |
112 |
|
112 | |||
113 | author_email = 'ipython-dev@scipy.org' |
|
113 | author_email = 'ipython-dev@scipy.org' | |
114 |
|
114 | |||
115 | url = 'http://ipython.scipy.org' |
|
115 | url = 'http://ipython.scipy.org' | |
116 |
|
116 | |||
117 | download_url = 'http://ipython.scipy.org/dist' |
|
117 | download_url = 'http://ipython.scipy.org/dist' | |
118 |
|
118 | |||
119 | platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME'] |
|
119 | platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME'] | |
120 |
|
120 | |||
121 | keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed'] |
|
121 | keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed'] |
This diff has been collapsed as it changes many lines, (1081 lines changed) Show them Hide them | |||||
@@ -1,1867 +1,2216 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 |
# Copyright |
|
3 | # Copyright Β© 2006-2009 Steven J. Bethard <steven.bethard@gmail.com>. | |
4 | # |
|
4 | # | |
5 | # Redistribution and use in source and binary forms, with or without |
|
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
6 | # modification, are permitted under the terms of the 3-clause BSD |
|
6 | # use this file except in compliance with the License. You may obtain a copy | |
7 | # license. No warranty expressed or implied. |
|
7 | # of the License at | |
8 | # For details, see the accompanying file LICENSE.txt. |
|
8 | # | |
|
9 | # http://www.apache.org/licenses/LICENSE-2.0 | |||
|
10 | # | |||
|
11 | # Unless required by applicable law or agreed to in writing, software | |||
|
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
|
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
|
14 | # License for the specific language governing permissions and limitations | |||
|
15 | # under the License. | |||
9 |
|
16 | |||
10 | """Command-line parsing library |
|
17 | """Command-line parsing library | |
11 |
|
18 | |||
12 | This module is an optparse-inspired command-line parsing library that: |
|
19 | This module is an optparse-inspired command-line parsing library that: | |
13 |
|
20 | |||
14 |
|
|
21 | - handles both optional and positional arguments | |
15 |
|
|
22 | - produces highly informative usage messages | |
16 |
|
|
23 | - supports parsers that dispatch to sub-parsers | |
17 |
|
24 | |||
18 | The following is a simple usage example that sums integers from the |
|
25 | The following is a simple usage example that sums integers from the | |
19 | command-line and writes the result to a file: |
|
26 | command-line and writes the result to a file:: | |
20 |
|
27 | |||
21 | parser = argparse.ArgumentParser( |
|
28 | parser = argparse.ArgumentParser( | |
22 | description='sum the integers at the command line') |
|
29 | description='sum the integers at the command line') | |
23 | parser.add_argument( |
|
30 | parser.add_argument( | |
24 | 'integers', metavar='int', nargs='+', type=int, |
|
31 | 'integers', metavar='int', nargs='+', type=int, | |
25 | help='an integer to be summed') |
|
32 | help='an integer to be summed') | |
26 | parser.add_argument( |
|
33 | parser.add_argument( | |
27 | '--log', default=sys.stdout, type=argparse.FileType('w'), |
|
34 | '--log', default=sys.stdout, type=argparse.FileType('w'), | |
28 | help='the file where the sum should be written') |
|
35 | help='the file where the sum should be written') | |
29 | args = parser.parse_args() |
|
36 | args = parser.parse_args() | |
30 | args.log.write('%s' % sum(args.integers)) |
|
37 | args.log.write('%s' % sum(args.integers)) | |
31 | args.log.close() |
|
38 | args.log.close() | |
32 |
|
39 | |||
33 | The module contains the following public classes: |
|
40 | The module contains the following public classes: | |
34 |
|
41 | |||
35 | ArgumentParser -- The main entry point for command-line parsing. As the |
|
42 | - ArgumentParser -- The main entry point for command-line parsing. As the | |
36 | example above shows, the add_argument() method is used to populate |
|
43 | example above shows, the add_argument() method is used to populate | |
37 | the parser with actions for optional and positional arguments. Then |
|
44 | the parser with actions for optional and positional arguments. Then | |
38 | the parse_args() method is invoked to convert the args at the |
|
45 | the parse_args() method is invoked to convert the args at the | |
39 | command-line into an object with attributes. |
|
46 | command-line into an object with attributes. | |
40 |
|
47 | |||
41 | ArgumentError -- The exception raised by ArgumentParser objects when |
|
48 | - ArgumentError -- The exception raised by ArgumentParser objects when | |
42 | there are errors with the parser's actions. Errors raised while |
|
49 | there are errors with the parser's actions. Errors raised while | |
43 | parsing the command-line are caught by ArgumentParser and emitted |
|
50 | parsing the command-line are caught by ArgumentParser and emitted | |
44 | as command-line messages. |
|
51 | as command-line messages. | |
45 |
|
52 | |||
46 | FileType -- A factory for defining types of files to be created. As the |
|
53 | - FileType -- A factory for defining types of files to be created. As the | |
47 | example above shows, instances of FileType are typically passed as |
|
54 | example above shows, instances of FileType are typically passed as | |
48 | the type= argument of add_argument() calls. |
|
55 | the type= argument of add_argument() calls. | |
49 |
|
56 | |||
50 | Action -- The base class for parser actions. Typically actions are |
|
57 | - Action -- The base class for parser actions. Typically actions are | |
51 | selected by passing strings like 'store_true' or 'append_const' to |
|
58 | selected by passing strings like 'store_true' or 'append_const' to | |
52 | the action= argument of add_argument(). However, for greater |
|
59 | the action= argument of add_argument(). However, for greater | |
53 | customization of ArgumentParser actions, subclasses of Action may |
|
60 | customization of ArgumentParser actions, subclasses of Action may | |
54 | be defined and passed as the action= argument. |
|
61 | be defined and passed as the action= argument. | |
55 |
|
62 | |||
56 |
HelpFormatter, RawDescriptionHelpFormatter |
|
63 | - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, | |
|
64 | ArgumentDefaultsHelpFormatter -- Formatter classes which | |||
57 | may be passed as the formatter_class= argument to the |
|
65 | may be passed as the formatter_class= argument to the | |
58 |
ArgumentParser constructor. HelpFormatter is the default, |
|
66 | ArgumentParser constructor. HelpFormatter is the default, | |
59 |
RawDescriptionHelpFormatter tell |
|
67 | RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser | |
60 | line-wrapping on description text. |
|
68 | not to change the formatting for help text, and | |
|
69 | ArgumentDefaultsHelpFormatter adds information about argument defaults | |||
|
70 | to the help. | |||
61 |
|
71 | |||
62 | All other classes in this module are considered implementation details. |
|
72 | All other classes in this module are considered implementation details. | |
63 | (Also note that HelpFormatter and RawDescriptionHelpFormatter are only |
|
73 | (Also note that HelpFormatter and RawDescriptionHelpFormatter are only | |
64 | considered public as object names -- the API of the formatter objects is |
|
74 | considered public as object names -- the API of the formatter objects is | |
65 | still considered an implementation detail.) |
|
75 | still considered an implementation detail.) | |
66 | """ |
|
76 | """ | |
67 |
|
77 | |||
68 |
__version__ = ' |
|
78 | __version__ = '1.0' | |
69 |
|
79 | __all__ = [ | ||
|
80 | 'ArgumentParser', | |||
|
81 | 'ArgumentError', | |||
|
82 | 'Namespace', | |||
|
83 | 'Action', | |||
|
84 | 'FileType', | |||
|
85 | 'HelpFormatter', | |||
|
86 | 'RawDescriptionHelpFormatter', | |||
|
87 | 'RawTextHelpFormatter' | |||
|
88 | 'ArgumentDefaultsHelpFormatter', | |||
|
89 | ] | |||
|
90 | ||||
|
91 | ||||
|
92 | import copy as _copy | |||
70 | import os as _os |
|
93 | import os as _os | |
71 | import re as _re |
|
94 | import re as _re | |
72 | import sys as _sys |
|
95 | import sys as _sys | |
73 | import textwrap as _textwrap |
|
96 | import textwrap as _textwrap | |
74 |
|
97 | |||
75 | from gettext import gettext as _ |
|
98 | from gettext import gettext as _ | |
76 |
|
99 | |||
|
100 | try: | |||
|
101 | _set = set | |||
|
102 | except NameError: | |||
|
103 | from sets import Set as _set | |||
|
104 | ||||
|
105 | try: | |||
|
106 | _basestring = basestring | |||
|
107 | except NameError: | |||
|
108 | _basestring = str | |||
|
109 | ||||
|
110 | try: | |||
|
111 | _sorted = sorted | |||
|
112 | except NameError: | |||
|
113 | ||||
|
114 | def _sorted(iterable, reverse=False): | |||
|
115 | result = list(iterable) | |||
|
116 | result.sort() | |||
|
117 | if reverse: | |||
|
118 | result.reverse() | |||
|
119 | return result | |||
|
120 | ||||
|
121 | ||||
77 | SUPPRESS = '==SUPPRESS==' |
|
122 | SUPPRESS = '==SUPPRESS==' | |
78 |
|
123 | |||
79 | OPTIONAL = '?' |
|
124 | OPTIONAL = '?' | |
80 | ZERO_OR_MORE = '*' |
|
125 | ZERO_OR_MORE = '*' | |
81 | ONE_OR_MORE = '+' |
|
126 | ONE_OR_MORE = '+' | |
82 | PARSER = '==PARSER==' |
|
127 | PARSER = '==PARSER==' | |
83 |
|
128 | |||
84 | # ============================= |
|
129 | # ============================= | |
85 | # Utility functions and classes |
|
130 | # Utility functions and classes | |
86 | # ============================= |
|
131 | # ============================= | |
87 |
|
132 | |||
88 | class _AttributeHolder(object): |
|
133 | class _AttributeHolder(object): | |
89 | """Abstract base class that provides __repr__. |
|
134 | """Abstract base class that provides __repr__. | |
90 |
|
135 | |||
91 | The __repr__ method returns a string in the format: |
|
136 | The __repr__ method returns a string in the format:: | |
92 | ClassName(attr=name, attr=name, ...) |
|
137 | ClassName(attr=name, attr=name, ...) | |
93 | The attributes are determined either by a class-level attribute, |
|
138 | The attributes are determined either by a class-level attribute, | |
94 | '_kwarg_names', or by inspecting the instance __dict__. |
|
139 | '_kwarg_names', or by inspecting the instance __dict__. | |
95 | """ |
|
140 | """ | |
96 |
|
141 | |||
97 | def __repr__(self): |
|
142 | def __repr__(self): | |
98 | type_name = type(self).__name__ |
|
143 | type_name = type(self).__name__ | |
99 | arg_strings = [] |
|
144 | arg_strings = [] | |
100 | for arg in self._get_args(): |
|
145 | for arg in self._get_args(): | |
101 | arg_strings.append(repr(arg)) |
|
146 | arg_strings.append(repr(arg)) | |
102 | for name, value in self._get_kwargs(): |
|
147 | for name, value in self._get_kwargs(): | |
103 | arg_strings.append('%s=%r' % (name, value)) |
|
148 | arg_strings.append('%s=%r' % (name, value)) | |
104 | return '%s(%s)' % (type_name, ', '.join(arg_strings)) |
|
149 | return '%s(%s)' % (type_name, ', '.join(arg_strings)) | |
105 |
|
150 | |||
106 | def _get_kwargs(self): |
|
151 | def _get_kwargs(self): | |
107 | return sorted(self.__dict__.items()) |
|
152 | return _sorted(self.__dict__.items()) | |
108 |
|
153 | |||
109 | def _get_args(self): |
|
154 | def _get_args(self): | |
110 | return [] |
|
155 | return [] | |
111 |
|
156 | |||
|
157 | ||||
112 | def _ensure_value(namespace, name, value): |
|
158 | def _ensure_value(namespace, name, value): | |
113 | if getattr(namespace, name, None) is None: |
|
159 | if getattr(namespace, name, None) is None: | |
114 | setattr(namespace, name, value) |
|
160 | setattr(namespace, name, value) | |
115 | return getattr(namespace, name) |
|
161 | return getattr(namespace, name) | |
116 |
|
||||
117 |
|
162 | |||
118 |
|
163 | |||
119 | # =============== |
|
164 | # =============== | |
120 | # Formatting Help |
|
165 | # Formatting Help | |
121 | # =============== |
|
166 | # =============== | |
122 |
|
167 | |||
123 | class HelpFormatter(object): |
|
168 | class HelpFormatter(object): | |
|
169 | """Formatter for generating usage messages and argument help strings. | |||
|
170 | ||||
|
171 | Only the name of this class is considered a public API. All the methods | |||
|
172 | provided by the class are considered an implementation detail. | |||
|
173 | """ | |||
124 |
|
174 | |||
125 | def __init__(self, |
|
175 | def __init__(self, | |
126 | prog, |
|
176 | prog, | |
127 | indent_increment=2, |
|
177 | indent_increment=2, | |
128 | max_help_position=24, |
|
178 | max_help_position=24, | |
129 | width=None): |
|
179 | width=None): | |
130 |
|
180 | |||
131 | # default setting for width |
|
181 | # default setting for width | |
132 | if width is None: |
|
182 | if width is None: | |
133 | try: |
|
183 | try: | |
134 | width = int(_os.environ['COLUMNS']) |
|
184 | width = int(_os.environ['COLUMNS']) | |
135 | except (KeyError, ValueError): |
|
185 | except (KeyError, ValueError): | |
136 | width = 80 |
|
186 | width = 80 | |
137 | width -= 2 |
|
187 | width -= 2 | |
138 |
|
188 | |||
139 | self._prog = prog |
|
189 | self._prog = prog | |
140 | self._indent_increment = indent_increment |
|
190 | self._indent_increment = indent_increment | |
141 | self._max_help_position = max_help_position |
|
191 | self._max_help_position = max_help_position | |
142 | self._width = width |
|
192 | self._width = width | |
143 |
|
193 | |||
144 | self._current_indent = 0 |
|
194 | self._current_indent = 0 | |
145 | self._level = 0 |
|
195 | self._level = 0 | |
146 | self._action_max_length = 0 |
|
196 | self._action_max_length = 0 | |
147 |
|
197 | |||
148 | self._root_section = self._Section(self, None) |
|
198 | self._root_section = self._Section(self, None) | |
149 | self._current_section = self._root_section |
|
199 | self._current_section = self._root_section | |
150 |
|
200 | |||
151 | self._whitespace_matcher = _re.compile(r'\s+') |
|
201 | self._whitespace_matcher = _re.compile(r'\s+') | |
152 | self._long_break_matcher = _re.compile(r'\n\n\n+') |
|
202 | self._long_break_matcher = _re.compile(r'\n\n\n+') | |
153 |
|
203 | |||
154 | # =============================== |
|
204 | # =============================== | |
155 | # Section and indentation methods |
|
205 | # Section and indentation methods | |
156 | # =============================== |
|
206 | # =============================== | |
157 |
|
||||
158 | def _indent(self): |
|
207 | def _indent(self): | |
159 | self._current_indent += self._indent_increment |
|
208 | self._current_indent += self._indent_increment | |
160 | self._level += 1 |
|
209 | self._level += 1 | |
161 |
|
210 | |||
162 | def _dedent(self): |
|
211 | def _dedent(self): | |
163 | self._current_indent -= self._indent_increment |
|
212 | self._current_indent -= self._indent_increment | |
164 | assert self._current_indent >= 0, 'Indent decreased below 0.' |
|
213 | assert self._current_indent >= 0, 'Indent decreased below 0.' | |
165 | self._level -= 1 |
|
214 | self._level -= 1 | |
166 |
|
215 | |||
167 | class _Section(object): |
|
216 | class _Section(object): | |
|
217 | ||||
168 | def __init__(self, formatter, parent, heading=None): |
|
218 | def __init__(self, formatter, parent, heading=None): | |
169 | self.formatter = formatter |
|
219 | self.formatter = formatter | |
170 | self.parent = parent |
|
220 | self.parent = parent | |
171 | self.heading = heading |
|
221 | self.heading = heading | |
172 | self.items = [] |
|
222 | self.items = [] | |
173 |
|
223 | |||
174 | def format_help(self): |
|
224 | def format_help(self): | |
175 | # format the indented section |
|
225 | # format the indented section | |
176 | if self.parent is not None: |
|
226 | if self.parent is not None: | |
177 | self.formatter._indent() |
|
227 | self.formatter._indent() | |
178 | join = self.formatter._join_parts |
|
228 | join = self.formatter._join_parts | |
179 | for func, args in self.items: |
|
229 | for func, args in self.items: | |
180 | func(*args) |
|
230 | func(*args) | |
181 | item_help = join(func(*args) for func, args in self.items) |
|
231 | item_help = join([func(*args) for func, args in self.items]) | |
182 | if self.parent is not None: |
|
232 | if self.parent is not None: | |
183 | self.formatter._dedent() |
|
233 | self.formatter._dedent() | |
184 |
|
234 | |||
185 |
# return nothing if the section was empty |
|
235 | # return nothing if the section was empty | |
186 | if not item_help: |
|
236 | if not item_help: | |
187 | return '' |
|
237 | return '' | |
188 |
|
238 | |||
189 | # add the heading if the section was non-empty |
|
239 | # add the heading if the section was non-empty | |
190 | if self.heading is not SUPPRESS and self.heading is not None: |
|
240 | if self.heading is not SUPPRESS and self.heading is not None: | |
191 | current_indent = self.formatter._current_indent |
|
241 | current_indent = self.formatter._current_indent | |
192 | heading = '%*s%s:\n' % (current_indent, '', self.heading) |
|
242 | heading = '%*s%s:\n' % (current_indent, '', self.heading) | |
193 | else: |
|
243 | else: | |
194 | heading = '' |
|
244 | heading = '' | |
195 |
|
245 | |||
196 | # join the section-initial newline, the heading and the help |
|
246 | # join the section-initial newline, the heading and the help | |
197 | return join(['\n', heading, item_help, '\n']) |
|
247 | return join(['\n', heading, item_help, '\n']) | |
198 |
|
248 | |||
199 | def _add_item(self, func, args): |
|
249 | def _add_item(self, func, args): | |
200 | self._current_section.items.append((func, args)) |
|
250 | self._current_section.items.append((func, args)) | |
201 |
|
251 | |||
202 | # ======================== |
|
252 | # ======================== | |
203 | # Message building methods |
|
253 | # Message building methods | |
204 | # ======================== |
|
254 | # ======================== | |
205 |
|
||||
206 | def start_section(self, heading): |
|
255 | def start_section(self, heading): | |
207 | self._indent() |
|
256 | self._indent() | |
208 | section = self._Section(self, self._current_section, heading) |
|
257 | section = self._Section(self, self._current_section, heading) | |
209 | self._add_item(section.format_help, []) |
|
258 | self._add_item(section.format_help, []) | |
210 | self._current_section = section |
|
259 | self._current_section = section | |
211 |
|
260 | |||
212 | def end_section(self): |
|
261 | def end_section(self): | |
213 | self._current_section = self._current_section.parent |
|
262 | self._current_section = self._current_section.parent | |
214 | self._dedent() |
|
263 | self._dedent() | |
215 |
|
264 | |||
216 | def add_text(self, text): |
|
265 | def add_text(self, text): | |
217 | if text is not SUPPRESS and text is not None: |
|
266 | if text is not SUPPRESS and text is not None: | |
218 | self._add_item(self._format_text, [text]) |
|
267 | self._add_item(self._format_text, [text]) | |
219 |
|
268 | |||
220 |
def add_usage(self, usage, |
|
269 | def add_usage(self, usage, actions, groups, prefix=None): | |
221 | if usage is not SUPPRESS: |
|
270 | if usage is not SUPPRESS: | |
222 |
args = usage, |
|
271 | args = usage, actions, groups, prefix | |
223 | self._add_item(self._format_usage, args) |
|
272 | self._add_item(self._format_usage, args) | |
224 |
|
273 | |||
225 | def add_argument(self, action): |
|
274 | def add_argument(self, action): | |
226 | if action.help is not SUPPRESS: |
|
275 | if action.help is not SUPPRESS: | |
227 |
|
276 | |||
228 |
# find all invocations |
|
277 | # find all invocations | |
229 | get_invocation = self._format_action_invocation |
|
278 | get_invocation = self._format_action_invocation | |
230 | invocations = [get_invocation(action)] |
|
279 | invocations = [get_invocation(action)] | |
231 | for subaction in self._iter_indented_subactions(action): |
|
280 | for subaction in self._iter_indented_subactions(action): | |
232 | invocations.append(get_invocation(subaction)) |
|
281 | invocations.append(get_invocation(subaction)) | |
233 |
|
282 | |||
234 | # update the maximum item length |
|
283 | # update the maximum item length | |
235 | invocation_length = max(len(s) for s in invocations) |
|
284 | invocation_length = max([len(s) for s in invocations]) | |
236 | action_length = invocation_length + self._current_indent |
|
285 | action_length = invocation_length + self._current_indent | |
237 | self._action_max_length = max(self._action_max_length, |
|
286 | self._action_max_length = max(self._action_max_length, | |
238 | action_length) |
|
287 | action_length) | |
239 |
|
288 | |||
240 |
# add the item to the list |
|
289 | # add the item to the list | |
241 | self._add_item(self._format_action, [action]) |
|
290 | self._add_item(self._format_action, [action]) | |
242 |
|
291 | |||
243 | def add_arguments(self, actions): |
|
292 | def add_arguments(self, actions): | |
244 | for action in actions: |
|
293 | for action in actions: | |
245 | self.add_argument(action) |
|
294 | self.add_argument(action) | |
246 |
|
295 | |||
247 | # ======================= |
|
296 | # ======================= | |
248 | # Help-formatting methods |
|
297 | # Help-formatting methods | |
249 | # ======================= |
|
298 | # ======================= | |
250 |
|
||||
251 | def format_help(self): |
|
299 | def format_help(self): | |
252 | help = self._root_section.format_help() % dict(prog=self._prog) |
|
300 | help = self._root_section.format_help() % dict(prog=self._prog) | |
253 | if help: |
|
301 | if help: | |
254 | help = self._long_break_matcher.sub('\n\n', help) |
|
302 | help = self._long_break_matcher.sub('\n\n', help) | |
255 | help = help.strip('\n') + '\n' |
|
303 | help = help.strip('\n') + '\n' | |
256 | return help |
|
304 | return help | |
257 |
|
305 | |||
258 | def _join_parts(self, part_strings): |
|
306 | def _join_parts(self, part_strings): | |
259 | return ''.join(part |
|
307 | return ''.join([part | |
260 | for part in part_strings |
|
308 | for part in part_strings | |
261 | if part and part is not SUPPRESS) |
|
309 | if part and part is not SUPPRESS]) | |
262 |
|
310 | |||
263 |
def _format_usage(self, usage, |
|
311 | def _format_usage(self, usage, actions, groups, prefix): | |
264 | if prefix is None: |
|
312 | if prefix is None: | |
265 | prefix = _('usage: ') |
|
313 | prefix = _('usage: ') | |
266 |
|
314 | |||
267 | # if no optionals or positionals are available, usage is just prog |
|
315 | # if no optionals or positionals are available, usage is just prog | |
268 |
if usage is None and not |
|
316 | if usage is None and not actions: | |
269 | usage = '%(prog)s' |
|
317 | usage = '%(prog)s' | |
270 |
|
318 | |||
271 | # if optionals and positionals are available, calculate usage |
|
319 | # if optionals and positionals are available, calculate usage | |
272 | elif usage is None: |
|
320 | elif usage is None: | |
273 | usage = '%(prog)s' % dict(prog=self._prog) |
|
321 | usage = '%(prog)s' % dict(prog=self._prog) | |
274 |
|
322 | |||
|
323 | # split optionals from positionals | |||
|
324 | optionals = [] | |||
|
325 | positionals = [] | |||
|
326 | for action in actions: | |||
|
327 | if action.option_strings: | |||
|
328 | optionals.append(action) | |||
|
329 | else: | |||
|
330 | positionals.append(action) | |||
|
331 | ||||
275 | # determine width of "usage: PROG" and width of text |
|
332 | # determine width of "usage: PROG" and width of text | |
276 | prefix_width = len(prefix) + len(usage) + 1 |
|
333 | prefix_width = len(prefix) + len(usage) + 1 | |
277 | prefix_indent = self._current_indent + prefix_width |
|
334 | prefix_indent = self._current_indent + prefix_width | |
278 | text_width = self._width - self._current_indent |
|
335 | text_width = self._width - self._current_indent | |
279 |
|
336 | |||
280 | # put them on one line if they're short enough |
|
337 | # put them on one line if they're short enough | |
281 | format = self._format_actions_usage |
|
338 | format = self._format_actions_usage | |
282 | action_usage = format(optionals + positionals) |
|
339 | action_usage = format(optionals + positionals, groups) | |
283 | if prefix_width + len(action_usage) + 1 < text_width: |
|
340 | if prefix_width + len(action_usage) + 1 < text_width: | |
284 | usage = '%s %s' % (usage, action_usage) |
|
341 | usage = '%s %s' % (usage, action_usage) | |
285 |
|
342 | |||
286 | # if they're long, wrap optionals and positionals individually |
|
343 | # if they're long, wrap optionals and positionals individually | |
287 | else: |
|
344 | else: | |
288 | optional_usage = format(optionals) |
|
345 | optional_usage = format(optionals, groups) | |
289 | positional_usage = format(positionals) |
|
346 | positional_usage = format(positionals, groups) | |
290 | indent = ' ' * prefix_indent |
|
347 | indent = ' ' * prefix_indent | |
291 |
|
348 | |||
292 | # usage is made of PROG, optionals and positionals |
|
349 | # usage is made of PROG, optionals and positionals | |
293 | parts = [usage, ' '] |
|
350 | parts = [usage, ' '] | |
294 |
|
351 | |||
295 | # options always get added right after PROG |
|
352 | # options always get added right after PROG | |
296 | if optional_usage: |
|
353 | if optional_usage: | |
297 | parts.append(_textwrap.fill( |
|
354 | parts.append(_textwrap.fill( | |
298 | optional_usage, text_width, |
|
355 | optional_usage, text_width, | |
299 | initial_indent=indent, |
|
356 | initial_indent=indent, | |
300 | subsequent_indent=indent).lstrip()) |
|
357 | subsequent_indent=indent).lstrip()) | |
301 |
|
358 | |||
302 | # if there were options, put arguments on the next line |
|
359 | # if there were options, put arguments on the next line | |
303 | # otherwise, start them right after PROG |
|
360 | # otherwise, start them right after PROG | |
304 | if positional_usage: |
|
361 | if positional_usage: | |
305 | part = _textwrap.fill( |
|
362 | part = _textwrap.fill( | |
306 | positional_usage, text_width, |
|
363 | positional_usage, text_width, | |
307 | initial_indent=indent, |
|
364 | initial_indent=indent, | |
308 | subsequent_indent=indent).lstrip() |
|
365 | subsequent_indent=indent).lstrip() | |
309 | if optional_usage: |
|
366 | if optional_usage: | |
310 | part = '\n' + indent + part |
|
367 | part = '\n' + indent + part | |
311 | parts.append(part) |
|
368 | parts.append(part) | |
312 | usage = ''.join(parts) |
|
369 | usage = ''.join(parts) | |
313 |
|
370 | |||
314 | # prefix with 'usage:' |
|
371 | # prefix with 'usage:' | |
315 | return '%s%s\n\n' % (prefix, usage) |
|
372 | return '%s%s\n\n' % (prefix, usage) | |
316 |
|
373 | |||
317 | def _format_actions_usage(self, actions): |
|
374 | def _format_actions_usage(self, actions, groups): | |
318 | parts = [] |
|
375 | # find group indices and identify actions in groups | |
319 | for action in actions: |
|
376 | group_actions = _set() | |
320 | if action.help is SUPPRESS: |
|
377 | inserts = {} | |
|
378 | for group in groups: | |||
|
379 | try: | |||
|
380 | start = actions.index(group._group_actions[0]) | |||
|
381 | except ValueError: | |||
321 | continue |
|
382 | continue | |
|
383 | else: | |||
|
384 | end = start + len(group._group_actions) | |||
|
385 | if actions[start:end] == group._group_actions: | |||
|
386 | for action in group._group_actions: | |||
|
387 | group_actions.add(action) | |||
|
388 | if not group.required: | |||
|
389 | inserts[start] = '[' | |||
|
390 | inserts[end] = ']' | |||
|
391 | else: | |||
|
392 | inserts[start] = '(' | |||
|
393 | inserts[end] = ')' | |||
|
394 | for i in range(start + 1, end): | |||
|
395 | inserts[i] = '|' | |||
322 |
|
396 | |||
323 |
|
|
397 | # collect all actions format strings | |
324 | if not action.option_strings: |
|
398 | parts = [] | |
325 | parts.append(self._format_args(action, action.dest)) |
|
399 | for i, action in enumerate(actions): | |
|
400 | ||||
|
401 | # suppressed arguments are marked with None | |||
|
402 | # remove | separators for suppressed arguments | |||
|
403 | if action.help is SUPPRESS: | |||
|
404 | parts.append(None) | |||
|
405 | if inserts.get(i) == '|': | |||
|
406 | inserts.pop(i) | |||
|
407 | elif inserts.get(i + 1) == '|': | |||
|
408 | inserts.pop(i + 1) | |||
|
409 | ||||
|
410 | # produce all arg strings | |||
|
411 | elif not action.option_strings: | |||
|
412 | part = self._format_args(action, action.dest) | |||
|
413 | ||||
|
414 | # if it's in a group, strip the outer [] | |||
|
415 | if action in group_actions: | |||
|
416 | if part[0] == '[' and part[-1] == ']': | |||
|
417 | part = part[1:-1] | |||
|
418 | ||||
|
419 | # add the action string to the list | |||
|
420 | parts.append(part) | |||
326 |
|
421 | |||
327 | # produce the first way to invoke the option in brackets |
|
422 | # produce the first way to invoke the option in brackets | |
328 | else: |
|
423 | else: | |
329 | option_string = action.option_strings[0] |
|
424 | option_string = action.option_strings[0] | |
330 |
|
425 | |||
331 | # if the Optional doesn't take a value, format is: |
|
426 | # if the Optional doesn't take a value, format is: | |
332 | # -s or --long |
|
427 | # -s or --long | |
333 | if action.nargs == 0: |
|
428 | if action.nargs == 0: | |
334 | part = '%s' % option_string |
|
429 | part = '%s' % option_string | |
335 |
|
430 | |||
336 | # if the Optional takes a value, format is: |
|
431 | # if the Optional takes a value, format is: | |
337 | # -s ARGS or --long ARGS |
|
432 | # -s ARGS or --long ARGS | |
338 | else: |
|
433 | else: | |
339 | default = action.dest.upper() |
|
434 | default = action.dest.upper() | |
340 | args_string = self._format_args(action, default) |
|
435 | args_string = self._format_args(action, default) | |
341 | part = '%s %s' % (option_string, args_string) |
|
436 | part = '%s %s' % (option_string, args_string) | |
342 |
|
437 | |||
343 | # make it look optional if it's not required |
|
438 | # make it look optional if it's not required or in a group | |
344 | if not action.required: |
|
439 | if not action.required and action not in group_actions: | |
345 | part = '[%s]' % part |
|
440 | part = '[%s]' % part | |
|
441 | ||||
|
442 | # add the action string to the list | |||
346 | parts.append(part) |
|
443 | parts.append(part) | |
347 |
|
444 | |||
348 | return ' '.join(parts) |
|
445 | # insert things at the necessary indices | |
|
446 | for i in _sorted(inserts, reverse=True): | |||
|
447 | parts[i:i] = [inserts[i]] | |||
|
448 | ||||
|
449 | # join all the action items with spaces | |||
|
450 | text = ' '.join([item for item in parts if item is not None]) | |||
|
451 | ||||
|
452 | # clean up separators for mutually exclusive groups | |||
|
453 | open = r'[\[(]' | |||
|
454 | close = r'[\])]' | |||
|
455 | text = _re.sub(r'(%s) ' % open, r'\1', text) | |||
|
456 | text = _re.sub(r' (%s)' % close, r'\1', text) | |||
|
457 | text = _re.sub(r'%s *%s' % (open, close), r'', text) | |||
|
458 | text = _re.sub(r'\(([^|]*)\)', r'\1', text) | |||
|
459 | text = text.strip() | |||
|
460 | ||||
|
461 | # return the text | |||
|
462 | return text | |||
349 |
|
463 | |||
350 | def _format_text(self, text): |
|
464 | def _format_text(self, text): | |
351 | text_width = self._width - self._current_indent |
|
465 | text_width = self._width - self._current_indent | |
352 | indent = ' ' * self._current_indent |
|
466 | indent = ' ' * self._current_indent | |
353 | return self._fill_text(text, text_width, indent) + '\n\n' |
|
467 | return self._fill_text(text, text_width, indent) + '\n\n' | |
354 |
|
468 | |||
355 | def _format_action(self, action): |
|
469 | def _format_action(self, action): | |
356 | # determine the required width and the entry label |
|
470 | # determine the required width and the entry label | |
357 | help_position = min(self._action_max_length + 2, |
|
471 | help_position = min(self._action_max_length + 2, | |
358 | self._max_help_position) |
|
472 | self._max_help_position) | |
359 | help_width = self._width - help_position |
|
473 | help_width = self._width - help_position | |
360 | action_width = help_position - self._current_indent - 2 |
|
474 | action_width = help_position - self._current_indent - 2 | |
361 | action_header = self._format_action_invocation(action) |
|
475 | action_header = self._format_action_invocation(action) | |
362 |
|
476 | |||
363 | # ho nelp; start on same line and add a final newline |
|
477 | # ho nelp; start on same line and add a final newline | |
364 | if not action.help: |
|
478 | if not action.help: | |
365 | tup = self._current_indent, '', action_header |
|
479 | tup = self._current_indent, '', action_header | |
366 | action_header = '%*s%s\n' % tup |
|
480 | action_header = '%*s%s\n' % tup | |
367 |
|
481 | |||
368 | # short action name; start on the same line and pad two spaces |
|
482 | # short action name; start on the same line and pad two spaces | |
369 | elif len(action_header) <= action_width: |
|
483 | elif len(action_header) <= action_width: | |
370 | tup = self._current_indent, '', action_width, action_header |
|
484 | tup = self._current_indent, '', action_width, action_header | |
371 | action_header = '%*s%-*s ' % tup |
|
485 | action_header = '%*s%-*s ' % tup | |
372 | indent_first = 0 |
|
486 | indent_first = 0 | |
373 |
|
487 | |||
374 | # long action name; start on the next line |
|
488 | # long action name; start on the next line | |
375 | else: |
|
489 | else: | |
376 | tup = self._current_indent, '', action_header |
|
490 | tup = self._current_indent, '', action_header | |
377 | action_header = '%*s%s\n' % tup |
|
491 | action_header = '%*s%s\n' % tup | |
378 | indent_first = help_position |
|
492 | indent_first = help_position | |
379 |
|
493 | |||
380 | # collect the pieces of the action help |
|
494 | # collect the pieces of the action help | |
381 | parts = [action_header] |
|
495 | parts = [action_header] | |
382 |
|
496 | |||
383 | # if there was help for the action, add lines of help text |
|
497 | # if there was help for the action, add lines of help text | |
384 | if action.help: |
|
498 | if action.help: | |
385 | help_text = self._expand_help(action) |
|
499 | help_text = self._expand_help(action) | |
386 | help_lines = self._split_lines(help_text, help_width) |
|
500 | help_lines = self._split_lines(help_text, help_width) | |
387 | parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) |
|
501 | parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) | |
388 | for line in help_lines[1:]: |
|
502 | for line in help_lines[1:]: | |
389 | parts.append('%*s%s\n' % (help_position, '', line)) |
|
503 | parts.append('%*s%s\n' % (help_position, '', line)) | |
390 |
|
504 | |||
391 | # or add a newline if the description doesn't end with one |
|
505 | # or add a newline if the description doesn't end with one | |
392 | elif not action_header.endswith('\n'): |
|
506 | elif not action_header.endswith('\n'): | |
393 | parts.append('\n') |
|
507 | parts.append('\n') | |
394 |
|
508 | |||
395 | # if there are any sub-actions, add their help as well |
|
509 | # if there are any sub-actions, add their help as well | |
396 | for subaction in self._iter_indented_subactions(action): |
|
510 | for subaction in self._iter_indented_subactions(action): | |
397 | parts.append(self._format_action(subaction)) |
|
511 | parts.append(self._format_action(subaction)) | |
398 |
|
512 | |||
399 |
# return a single string |
|
513 | # return a single string | |
400 | return self._join_parts(parts) |
|
514 | return self._join_parts(parts) | |
401 |
|
515 | |||
402 | def _format_action_invocation(self, action): |
|
516 | def _format_action_invocation(self, action): | |
403 | if not action.option_strings: |
|
517 | if not action.option_strings: | |
404 |
|
|
518 | metavar, = self._metavar_formatter(action, action.dest)(1) | |
|
519 | return metavar | |||
405 |
|
520 | |||
406 | else: |
|
521 | else: | |
407 | parts = [] |
|
522 | parts = [] | |
408 |
|
523 | |||
409 | # if the Optional doesn't take a value, format is: |
|
524 | # if the Optional doesn't take a value, format is: | |
410 | # -s, --long |
|
525 | # -s, --long | |
411 | if action.nargs == 0: |
|
526 | if action.nargs == 0: | |
412 | parts.extend(action.option_strings) |
|
527 | parts.extend(action.option_strings) | |
413 |
|
528 | |||
414 | # if the Optional takes a value, format is: |
|
529 | # if the Optional takes a value, format is: | |
415 | # -s ARGS, --long ARGS |
|
530 | # -s ARGS, --long ARGS | |
416 | else: |
|
531 | else: | |
417 | default = action.dest.upper() |
|
532 | default = action.dest.upper() | |
418 | args_string = self._format_args(action, default) |
|
533 | args_string = self._format_args(action, default) | |
419 | for option_string in action.option_strings: |
|
534 | for option_string in action.option_strings: | |
420 | parts.append('%s %s' % (option_string, args_string)) |
|
535 | parts.append('%s %s' % (option_string, args_string)) | |
421 |
|
536 | |||
422 | return ', '.join(parts) |
|
537 | return ', '.join(parts) | |
423 |
|
538 | |||
424 |
def _format |
|
539 | def _metavar_formatter(self, action, default_metavar): | |
425 | if action.metavar is not None: |
|
540 | if action.metavar is not None: | |
426 |
|
|
541 | result = action.metavar | |
427 | elif action.choices is not None: |
|
542 | elif action.choices is not None: | |
428 |
choice_strs = |
|
543 | choice_strs = [str(choice) for choice in action.choices] | |
429 |
|
|
544 | result = '{%s}' % ','.join(choice_strs) | |
430 | else: |
|
545 | else: | |
431 |
|
|
546 | result = default_metavar | |
432 | return name |
|
547 | ||
|
548 | def format(tuple_size): | |||
|
549 | if isinstance(result, tuple): | |||
|
550 | return result | |||
|
551 | else: | |||
|
552 | return (result, ) * tuple_size | |||
|
553 | return format | |||
433 |
|
554 | |||
434 | def _format_args(self, action, default_metavar): |
|
555 | def _format_args(self, action, default_metavar): | |
435 |
|
|
556 | get_metavar = self._metavar_formatter(action, default_metavar) | |
436 | if action.nargs is None: |
|
557 | if action.nargs is None: | |
437 |
result = |
|
558 | result = '%s' % get_metavar(1) | |
438 | elif action.nargs == OPTIONAL: |
|
559 | elif action.nargs == OPTIONAL: | |
439 |
result = '[%s]' % |
|
560 | result = '[%s]' % get_metavar(1) | |
440 | elif action.nargs == ZERO_OR_MORE: |
|
561 | elif action.nargs == ZERO_OR_MORE: | |
441 |
result = '[%s [%s ...]]' % ( |
|
562 | result = '[%s [%s ...]]' % get_metavar(2) | |
442 | elif action.nargs == ONE_OR_MORE: |
|
563 | elif action.nargs == ONE_OR_MORE: | |
443 |
result = '%s [%s ...]' % ( |
|
564 | result = '%s [%s ...]' % get_metavar(2) | |
444 | elif action.nargs is PARSER: |
|
565 | elif action.nargs is PARSER: | |
445 |
result = '%s ...' % |
|
566 | result = '%s ...' % get_metavar(1) | |
446 | else: |
|
567 | else: | |
447 |
|
|
568 | formats = ['%s' for _ in range(action.nargs)] | |
|
569 | result = ' '.join(formats) % get_metavar(action.nargs) | |||
448 | return result |
|
570 | return result | |
449 |
|
571 | |||
450 | def _expand_help(self, action): |
|
572 | def _expand_help(self, action): | |
451 | params = dict(vars(action), prog=self._prog) |
|
573 | params = dict(vars(action), prog=self._prog) | |
452 |
for name |
|
574 | for name in list(params): | |
453 |
if |
|
575 | if params[name] is SUPPRESS: | |
454 | del params[name] |
|
576 | del params[name] | |
455 | if params.get('choices') is not None: |
|
577 | if params.get('choices') is not None: | |
456 | choices_str = ', '.join(str(c) for c in params['choices']) |
|
578 | choices_str = ', '.join([str(c) for c in params['choices']]) | |
457 | params['choices'] = choices_str |
|
579 | params['choices'] = choices_str | |
458 |
return action |
|
580 | return self._get_help_string(action) % params | |
459 |
|
581 | |||
460 | def _iter_indented_subactions(self, action): |
|
582 | def _iter_indented_subactions(self, action): | |
461 | try: |
|
583 | try: | |
462 | get_subactions = action._get_subactions |
|
584 | get_subactions = action._get_subactions | |
463 | except AttributeError: |
|
585 | except AttributeError: | |
464 | pass |
|
586 | pass | |
465 | else: |
|
587 | else: | |
466 | self._indent() |
|
588 | self._indent() | |
467 | for subaction in get_subactions(): |
|
589 | for subaction in get_subactions(): | |
468 | yield subaction |
|
590 | yield subaction | |
469 | self._dedent() |
|
591 | self._dedent() | |
470 |
|
592 | |||
471 | def _split_lines(self, text, width): |
|
593 | def _split_lines(self, text, width): | |
472 | text = self._whitespace_matcher.sub(' ', text).strip() |
|
594 | text = self._whitespace_matcher.sub(' ', text).strip() | |
473 | return _textwrap.wrap(text, width) |
|
595 | return _textwrap.wrap(text, width) | |
474 |
|
596 | |||
475 | def _fill_text(self, text, width, indent): |
|
597 | def _fill_text(self, text, width, indent): | |
476 | text = self._whitespace_matcher.sub(' ', text).strip() |
|
598 | text = self._whitespace_matcher.sub(' ', text).strip() | |
477 | return _textwrap.fill(text, width, initial_indent=indent, |
|
599 | return _textwrap.fill(text, width, initial_indent=indent, | |
478 | subsequent_indent=indent) |
|
600 | subsequent_indent=indent) | |
479 |
|
601 | |||
|
602 | def _get_help_string(self, action): | |||
|
603 | return action.help | |||
|
604 | ||||
|
605 | ||||
480 | class RawDescriptionHelpFormatter(HelpFormatter): |
|
606 | class RawDescriptionHelpFormatter(HelpFormatter): | |
481 |
|
607 | """Help message formatter which retains any formatting in descriptions. | ||
|
608 | ||||
|
609 | Only the name of this class is considered a public API. All the methods | |||
|
610 | provided by the class are considered an implementation detail. | |||
|
611 | """ | |||
|
612 | ||||
482 | def _fill_text(self, text, width, indent): |
|
613 | def _fill_text(self, text, width, indent): | |
483 | return ''.join(indent + line for line in text.splitlines(True)) |
|
614 | return ''.join([indent + line for line in text.splitlines(True)]) | |
|
615 | ||||
484 |
|
616 | |||
485 | class RawTextHelpFormatter(RawDescriptionHelpFormatter): |
|
617 | class RawTextHelpFormatter(RawDescriptionHelpFormatter): | |
486 |
|
618 | """Help message formatter which retains formatting of all help text. | ||
|
619 | ||||
|
620 | Only the name of this class is considered a public API. All the methods | |||
|
621 | provided by the class are considered an implementation detail. | |||
|
622 | """ | |||
|
623 | ||||
487 | def _split_lines(self, text, width): |
|
624 | def _split_lines(self, text, width): | |
488 | return text.splitlines() |
|
625 | return text.splitlines() | |
489 |
|
626 | |||
|
627 | ||||
|
628 | class ArgumentDefaultsHelpFormatter(HelpFormatter): | |||
|
629 | """Help message formatter which adds default values to argument help. | |||
|
630 | ||||
|
631 | Only the name of this class is considered a public API. All the methods | |||
|
632 | provided by the class are considered an implementation detail. | |||
|
633 | """ | |||
|
634 | ||||
|
635 | def _get_help_string(self, action): | |||
|
636 | help = action.help | |||
|
637 | if '%(default)' not in action.help: | |||
|
638 | if action.default is not SUPPRESS: | |||
|
639 | defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] | |||
|
640 | if action.option_strings or action.nargs in defaulting_nargs: | |||
|
641 | help += ' (default: %(default)s)' | |||
|
642 | return help | |||
|
643 | ||||
|
644 | ||||
490 | # ===================== |
|
645 | # ===================== | |
491 | # Options and Arguments |
|
646 | # Options and Arguments | |
492 | # ===================== |
|
647 | # ===================== | |
493 |
|
648 | |||
494 | class ArgumentError(Exception): |
|
649 | def _get_action_name(argument): | |
495 | """ArgumentError(message, argument) |
|
650 | if argument is None: | |
|
651 | return None | |||
|
652 | elif argument.option_strings: | |||
|
653 | return '/'.join(argument.option_strings) | |||
|
654 | elif argument.metavar not in (None, SUPPRESS): | |||
|
655 | return argument.metavar | |||
|
656 | elif argument.dest not in (None, SUPPRESS): | |||
|
657 | return argument.dest | |||
|
658 | else: | |||
|
659 | return None | |||
496 |
|
660 | |||
497 | Raised whenever there was an error creating or using an argument |
|
661 | ||
498 | (optional or positional). |
|
662 | class ArgumentError(Exception): | |
|
663 | """An error from creating or using an argument (optional or positional). | |||
499 |
|
664 | |||
500 | The string value of this exception is the message, augmented with |
|
665 | The string value of this exception is the message, augmented with | |
501 | information about the argument that caused it. |
|
666 | information about the argument that caused it. | |
502 | """ |
|
667 | """ | |
503 |
|
668 | |||
504 | def __init__(self, argument, message): |
|
669 | def __init__(self, argument, message): | |
505 | if argument.option_strings: |
|
670 | self.argument_name = _get_action_name(argument) | |
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 |
|
671 | self.message = message | |
514 |
|
672 | |||
515 | def __str__(self): |
|
673 | def __str__(self): | |
516 | if self.argument_name is None: |
|
674 | if self.argument_name is None: | |
517 | format = '%(message)s' |
|
675 | format = '%(message)s' | |
518 | else: |
|
676 | else: | |
519 | format = 'argument %(argument_name)s: %(message)s' |
|
677 | format = 'argument %(argument_name)s: %(message)s' | |
520 | return format % dict(message=self.message, |
|
678 | return format % dict(message=self.message, | |
521 | argument_name=self.argument_name) |
|
679 | argument_name=self.argument_name) | |
522 |
|
680 | |||
523 | # ============== |
|
681 | # ============== | |
524 | # Action classes |
|
682 | # Action classes | |
525 | # ============== |
|
683 | # ============== | |
526 |
|
684 | |||
527 | class Action(_AttributeHolder): |
|
685 | class Action(_AttributeHolder): | |
528 | """Action(*strings, **options) |
|
686 | """Information about how to convert command line strings to Python objects. | |
529 |
|
687 | |||
530 | Action objects hold the information necessary to convert a |
|
688 | Action objects are used by an ArgumentParser to represent the information | |
531 | set of command-line arguments (possibly including an initial option |
|
689 | needed to parse a single argument from one or more strings from the | |
532 | string) into the desired Python object(s). |
|
690 | command line. The keyword arguments to the Action constructor are also | |
|
691 | all attributes of Action instances. | |||
533 |
|
692 | |||
534 | Keyword Arguments: |
|
693 | Keyword Arguments: | |
535 |
|
694 | |||
536 | option_strings -- A list of command-line option strings which |
|
695 | - option_strings -- A list of command-line option strings which | |
537 | should be associated with this action. |
|
696 | should be associated with this action. | |
538 |
|
697 | |||
539 | dest -- The name of the attribute to hold the created object(s) |
|
698 | - dest -- The name of the attribute to hold the created object(s) | |
540 |
|
699 | |||
541 |
nargs -- The number of command-line arguments that should be |
|
700 | - nargs -- The number of command-line arguments that should be | |
542 |
By default, one argument will be consumed and a single |
|
701 | consumed. By default, one argument will be consumed and a single | |
543 | be produced. Other values include: |
|
702 | value will be produced. Other values include: | |
544 |
|
|
703 | - N (an integer) consumes N arguments (and produces a list) | |
545 |
|
|
704 | - '?' consumes zero or one arguments | |
546 |
|
|
705 | - '*' consumes zero or more arguments (and produces a list) | |
547 |
|
|
706 | - '+' consumes one or more arguments (and produces a list) | |
548 | Note that the difference between the default and nargs=1 is that |
|
707 | Note that the difference between the default and nargs=1 is that | |
549 | with the default, a single value will be produced, while with |
|
708 | with the default, a single value will be produced, while with | |
550 | nargs=1, a list containing a single value will be produced. |
|
709 | nargs=1, a list containing a single value will be produced. | |
551 |
|
710 | |||
552 | const -- The value to be produced if the option is specified and the |
|
711 | - const -- The value to be produced if the option is specified and the | |
553 | option uses an action that takes no values. |
|
712 | option uses an action that takes no values. | |
554 |
|
713 | |||
555 | default -- The value to be produced if the option is not specified. |
|
714 | - default -- The value to be produced if the option is not specified. | |
556 |
|
715 | |||
557 | type -- The type which the command-line arguments should be converted |
|
716 | - type -- The type which the command-line arguments should be converted | |
558 | to, should be one of 'string', 'int', 'float', 'complex' or a |
|
717 | to, should be one of 'string', 'int', 'float', 'complex' or a | |
559 | callable object that accepts a single string argument. If None, |
|
718 | callable object that accepts a single string argument. If None, | |
560 | 'string' is assumed. |
|
719 | 'string' is assumed. | |
561 |
|
720 | |||
562 | choices -- A container of values that should be allowed. If not None, |
|
721 | - choices -- A container of values that should be allowed. If not None, | |
563 | after a command-line argument has been converted to the appropriate |
|
722 | 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 |
|
723 | type, an exception will be raised if it is not a member of this | |
565 | collection. |
|
724 | collection. | |
566 |
|
725 | |||
567 |
required -- True if the action must always be specified at the |
|
726 | - required -- True if the action must always be specified at the | |
568 |
line. This is only meaningful for optional command-line |
|
727 | command line. This is only meaningful for optional command-line | |
569 |
|
728 | arguments. | ||
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 |
|
|
729 | ||
|
730 | - help -- The help string describing the argument. | |||
|
731 | ||||
|
732 | - metavar -- The name to be used for the option's argument with the | |||
|
733 | help string. If None, the 'dest' value will be used as the name. | |||
|
734 | """ | |||
576 |
|
735 | |||
577 | def __init__(self, |
|
736 | def __init__(self, | |
578 | option_strings, |
|
737 | option_strings, | |
579 | dest, |
|
738 | dest, | |
580 | nargs=None, |
|
739 | nargs=None, | |
581 | const=None, |
|
740 | const=None, | |
582 | default=None, |
|
741 | default=None, | |
583 | type=None, |
|
742 | type=None, | |
584 | choices=None, |
|
743 | choices=None, | |
585 | required=False, |
|
744 | required=False, | |
586 | help=None, |
|
745 | help=None, | |
587 | metavar=None): |
|
746 | metavar=None): | |
588 | self.option_strings = option_strings |
|
747 | self.option_strings = option_strings | |
589 | self.dest = dest |
|
748 | self.dest = dest | |
590 | self.nargs = nargs |
|
749 | self.nargs = nargs | |
591 | self.const = const |
|
750 | self.const = const | |
592 | self.default = default |
|
751 | self.default = default | |
593 | self.type = type |
|
752 | self.type = type | |
594 | self.choices = choices |
|
753 | self.choices = choices | |
595 | self.required = required |
|
754 | self.required = required | |
596 | self.help = help |
|
755 | self.help = help | |
597 | self.metavar = metavar |
|
756 | self.metavar = metavar | |
598 |
|
757 | |||
599 | def _get_kwargs(self): |
|
758 | def _get_kwargs(self): | |
600 | names = [ |
|
759 | names = [ | |
601 | 'option_strings', |
|
760 | 'option_strings', | |
602 | 'dest', |
|
761 | 'dest', | |
603 | 'nargs', |
|
762 | 'nargs', | |
604 | 'const', |
|
763 | 'const', | |
605 | 'default', |
|
764 | 'default', | |
606 | 'type', |
|
765 | 'type', | |
607 | 'choices', |
|
766 | 'choices', | |
608 | 'help', |
|
767 | 'help', | |
609 | 'metavar' |
|
768 | 'metavar', | |
610 | ] |
|
769 | ] | |
611 | return [(name, getattr(self, name)) for name in names] |
|
770 | return [(name, getattr(self, name)) for name in names] | |
612 |
|
771 | |||
613 | def __call__(self, parser, namespace, values, option_string=None): |
|
772 | def __call__(self, parser, namespace, values, option_string=None): | |
614 | raise NotImplementedError(_('.__call__() not defined')) |
|
773 | raise NotImplementedError(_('.__call__() not defined')) | |
615 |
|
774 | |||
|
775 | ||||
616 | class _StoreAction(Action): |
|
776 | class _StoreAction(Action): | |
|
777 | ||||
617 | def __init__(self, |
|
778 | def __init__(self, | |
618 | option_strings, |
|
779 | option_strings, | |
619 | dest, |
|
780 | dest, | |
620 | nargs=None, |
|
781 | nargs=None, | |
621 | const=None, |
|
782 | const=None, | |
622 | default=None, |
|
783 | default=None, | |
623 | type=None, |
|
784 | type=None, | |
624 | choices=None, |
|
785 | choices=None, | |
625 | required=False, |
|
786 | required=False, | |
626 | help=None, |
|
787 | help=None, | |
627 | metavar=None): |
|
788 | metavar=None): | |
628 | if nargs == 0: |
|
789 | if nargs == 0: | |
629 | raise ValueError('nargs must be > 0') |
|
790 | raise ValueError('nargs must be > 0') | |
630 | if const is not None and nargs != OPTIONAL: |
|
791 | if const is not None and nargs != OPTIONAL: | |
631 | raise ValueError('nargs must be %r to supply const' % OPTIONAL) |
|
792 | raise ValueError('nargs must be %r to supply const' % OPTIONAL) | |
632 | super(_StoreAction, self).__init__( |
|
793 | super(_StoreAction, self).__init__( | |
633 | option_strings=option_strings, |
|
794 | option_strings=option_strings, | |
634 | dest=dest, |
|
795 | dest=dest, | |
635 | nargs=nargs, |
|
796 | nargs=nargs, | |
636 | const=const, |
|
797 | const=const, | |
637 | default=default, |
|
798 | default=default, | |
638 | type=type, |
|
799 | type=type, | |
639 | choices=choices, |
|
800 | choices=choices, | |
640 | required=required, |
|
801 | required=required, | |
641 | help=help, |
|
802 | help=help, | |
642 | metavar=metavar) |
|
803 | metavar=metavar) | |
643 |
|
804 | |||
644 | def __call__(self, parser, namespace, values, option_string=None): |
|
805 | def __call__(self, parser, namespace, values, option_string=None): | |
645 | setattr(namespace, self.dest, values) |
|
806 | setattr(namespace, self.dest, values) | |
646 |
|
807 | |||
|
808 | ||||
647 | class _StoreConstAction(Action): |
|
809 | class _StoreConstAction(Action): | |
|
810 | ||||
648 | def __init__(self, |
|
811 | def __init__(self, | |
649 | option_strings, |
|
812 | option_strings, | |
650 | dest, |
|
813 | dest, | |
651 | const, |
|
814 | const, | |
652 | default=None, |
|
815 | default=None, | |
653 | required=False, |
|
816 | required=False, | |
654 | help=None, |
|
817 | help=None, | |
655 | metavar=None): |
|
818 | metavar=None): | |
656 | super(_StoreConstAction, self).__init__( |
|
819 | super(_StoreConstAction, self).__init__( | |
657 | option_strings=option_strings, |
|
820 | option_strings=option_strings, | |
658 | dest=dest, |
|
821 | dest=dest, | |
659 | nargs=0, |
|
822 | nargs=0, | |
660 | const=const, |
|
823 | const=const, | |
661 | default=default, |
|
824 | default=default, | |
662 | required=required, |
|
825 | required=required, | |
663 | help=help) |
|
826 | help=help) | |
664 |
|
827 | |||
665 | def __call__(self, parser, namespace, values, option_string=None): |
|
828 | def __call__(self, parser, namespace, values, option_string=None): | |
666 | setattr(namespace, self.dest, self.const) |
|
829 | setattr(namespace, self.dest, self.const) | |
667 |
|
830 | |||
|
831 | ||||
668 | class _StoreTrueAction(_StoreConstAction): |
|
832 | class _StoreTrueAction(_StoreConstAction): | |
|
833 | ||||
669 | def __init__(self, |
|
834 | def __init__(self, | |
670 | option_strings, |
|
835 | option_strings, | |
671 | dest, |
|
836 | dest, | |
672 | default=False, |
|
837 | default=False, | |
673 | required=False, |
|
838 | required=False, | |
674 | help=None): |
|
839 | help=None): | |
675 | super(_StoreTrueAction, self).__init__( |
|
840 | super(_StoreTrueAction, self).__init__( | |
676 | option_strings=option_strings, |
|
841 | option_strings=option_strings, | |
677 | dest=dest, |
|
842 | dest=dest, | |
678 | const=True, |
|
843 | const=True, | |
679 | default=default, |
|
844 | default=default, | |
680 | required=required, |
|
845 | required=required, | |
681 | help=help) |
|
846 | help=help) | |
682 |
|
847 | |||
|
848 | ||||
683 | class _StoreFalseAction(_StoreConstAction): |
|
849 | class _StoreFalseAction(_StoreConstAction): | |
|
850 | ||||
684 | def __init__(self, |
|
851 | def __init__(self, | |
685 | option_strings, |
|
852 | option_strings, | |
686 | dest, |
|
853 | dest, | |
687 | default=True, |
|
854 | default=True, | |
688 | required=False, |
|
855 | required=False, | |
689 | help=None): |
|
856 | help=None): | |
690 | super(_StoreFalseAction, self).__init__( |
|
857 | super(_StoreFalseAction, self).__init__( | |
691 | option_strings=option_strings, |
|
858 | option_strings=option_strings, | |
692 | dest=dest, |
|
859 | dest=dest, | |
693 | const=False, |
|
860 | const=False, | |
694 | default=default, |
|
861 | default=default, | |
695 | required=required, |
|
862 | required=required, | |
696 | help=help) |
|
863 | help=help) | |
697 |
|
864 | |||
|
865 | ||||
698 | class _AppendAction(Action): |
|
866 | class _AppendAction(Action): | |
|
867 | ||||
699 | def __init__(self, |
|
868 | def __init__(self, | |
700 | option_strings, |
|
869 | option_strings, | |
701 | dest, |
|
870 | dest, | |
702 | nargs=None, |
|
871 | nargs=None, | |
703 | const=None, |
|
872 | const=None, | |
704 | default=None, |
|
873 | default=None, | |
705 | type=None, |
|
874 | type=None, | |
706 | choices=None, |
|
875 | choices=None, | |
707 | required=False, |
|
876 | required=False, | |
708 | help=None, |
|
877 | help=None, | |
709 | metavar=None): |
|
878 | metavar=None): | |
710 | if nargs == 0: |
|
879 | if nargs == 0: | |
711 | raise ValueError('nargs must be > 0') |
|
880 | raise ValueError('nargs must be > 0') | |
712 | if const is not None and nargs != OPTIONAL: |
|
881 | if const is not None and nargs != OPTIONAL: | |
713 | raise ValueError('nargs must be %r to supply const' % OPTIONAL) |
|
882 | raise ValueError('nargs must be %r to supply const' % OPTIONAL) | |
714 | super(_AppendAction, self).__init__( |
|
883 | super(_AppendAction, self).__init__( | |
715 | option_strings=option_strings, |
|
884 | option_strings=option_strings, | |
716 | dest=dest, |
|
885 | dest=dest, | |
717 | nargs=nargs, |
|
886 | nargs=nargs, | |
718 | const=const, |
|
887 | const=const, | |
719 | default=default, |
|
888 | default=default, | |
720 | type=type, |
|
889 | type=type, | |
721 | choices=choices, |
|
890 | choices=choices, | |
722 | required=required, |
|
891 | required=required, | |
723 | help=help, |
|
892 | help=help, | |
724 | metavar=metavar) |
|
893 | metavar=metavar) | |
725 |
|
894 | |||
726 | def __call__(self, parser, namespace, values, option_string=None): |
|
895 | def __call__(self, parser, namespace, values, option_string=None): | |
727 |
_ensure_value(namespace, self.dest, []) |
|
896 | items = _copy.copy(_ensure_value(namespace, self.dest, [])) | |
|
897 | items.append(values) | |||
|
898 | setattr(namespace, self.dest, items) | |||
|
899 | ||||
728 |
|
900 | |||
729 | class _AppendConstAction(Action): |
|
901 | class _AppendConstAction(Action): | |
|
902 | ||||
730 | def __init__(self, |
|
903 | def __init__(self, | |
731 | option_strings, |
|
904 | option_strings, | |
732 | dest, |
|
905 | dest, | |
733 | const, |
|
906 | const, | |
734 | default=None, |
|
907 | default=None, | |
735 | required=False, |
|
908 | required=False, | |
736 | help=None, |
|
909 | help=None, | |
737 | metavar=None): |
|
910 | metavar=None): | |
738 | super(_AppendConstAction, self).__init__( |
|
911 | super(_AppendConstAction, self).__init__( | |
739 | option_strings=option_strings, |
|
912 | option_strings=option_strings, | |
740 | dest=dest, |
|
913 | dest=dest, | |
741 | nargs=0, |
|
914 | nargs=0, | |
742 | const=const, |
|
915 | const=const, | |
743 | default=default, |
|
916 | default=default, | |
744 | required=required, |
|
917 | required=required, | |
745 | help=help, |
|
918 | help=help, | |
746 | metavar=metavar) |
|
919 | metavar=metavar) | |
747 |
|
920 | |||
748 | def __call__(self, parser, namespace, values, option_string=None): |
|
921 | def __call__(self, parser, namespace, values, option_string=None): | |
749 |
_ensure_value(namespace, self.dest, []) |
|
922 | items = _copy.copy(_ensure_value(namespace, self.dest, [])) | |
|
923 | items.append(self.const) | |||
|
924 | setattr(namespace, self.dest, items) | |||
|
925 | ||||
750 |
|
926 | |||
751 | class _CountAction(Action): |
|
927 | class _CountAction(Action): | |
|
928 | ||||
752 | def __init__(self, |
|
929 | def __init__(self, | |
753 | option_strings, |
|
930 | option_strings, | |
754 | dest, |
|
931 | dest, | |
755 | default=None, |
|
932 | default=None, | |
756 | required=False, |
|
933 | required=False, | |
757 | help=None): |
|
934 | help=None): | |
758 | super(_CountAction, self).__init__( |
|
935 | super(_CountAction, self).__init__( | |
759 | option_strings=option_strings, |
|
936 | option_strings=option_strings, | |
760 | dest=dest, |
|
937 | dest=dest, | |
761 | nargs=0, |
|
938 | nargs=0, | |
762 | default=default, |
|
939 | default=default, | |
763 | required=required, |
|
940 | required=required, | |
764 | help=help) |
|
941 | help=help) | |
765 |
|
942 | |||
766 | def __call__(self, parser, namespace, values, option_string=None): |
|
943 | def __call__(self, parser, namespace, values, option_string=None): | |
767 | new_count = _ensure_value(namespace, self.dest, 0) + 1 |
|
944 | new_count = _ensure_value(namespace, self.dest, 0) + 1 | |
768 | setattr(namespace, self.dest, new_count) |
|
945 | setattr(namespace, self.dest, new_count) | |
769 |
|
946 | |||
|
947 | ||||
770 | class _HelpAction(Action): |
|
948 | class _HelpAction(Action): | |
|
949 | ||||
771 | def __init__(self, |
|
950 | def __init__(self, | |
772 | option_strings, |
|
951 | option_strings, | |
773 | dest=SUPPRESS, |
|
952 | dest=SUPPRESS, | |
774 | default=SUPPRESS, |
|
953 | default=SUPPRESS, | |
775 | help=None): |
|
954 | help=None): | |
776 | super(_HelpAction, self).__init__( |
|
955 | super(_HelpAction, self).__init__( | |
777 | option_strings=option_strings, |
|
956 | option_strings=option_strings, | |
778 | dest=dest, |
|
957 | dest=dest, | |
779 | default=default, |
|
958 | default=default, | |
780 | nargs=0, |
|
959 | nargs=0, | |
781 | help=help) |
|
960 | help=help) | |
782 |
|
961 | |||
783 | def __call__(self, parser, namespace, values, option_string=None): |
|
962 | def __call__(self, parser, namespace, values, option_string=None): | |
784 | parser.print_help() |
|
963 | parser.print_help() | |
785 | parser.exit() |
|
964 | parser.exit() | |
786 |
|
965 | |||
|
966 | ||||
787 | class _VersionAction(Action): |
|
967 | class _VersionAction(Action): | |
|
968 | ||||
788 | def __init__(self, |
|
969 | def __init__(self, | |
789 | option_strings, |
|
970 | option_strings, | |
790 | dest=SUPPRESS, |
|
971 | dest=SUPPRESS, | |
791 | default=SUPPRESS, |
|
972 | default=SUPPRESS, | |
792 | help=None): |
|
973 | help=None): | |
793 | super(_VersionAction, self).__init__( |
|
974 | super(_VersionAction, self).__init__( | |
794 | option_strings=option_strings, |
|
975 | option_strings=option_strings, | |
795 | dest=dest, |
|
976 | dest=dest, | |
796 | default=default, |
|
977 | default=default, | |
797 | nargs=0, |
|
978 | nargs=0, | |
798 | help=help) |
|
979 | help=help) | |
799 |
|
980 | |||
800 | def __call__(self, parser, namespace, values, option_string=None): |
|
981 | def __call__(self, parser, namespace, values, option_string=None): | |
801 | parser.print_version() |
|
982 | parser.print_version() | |
802 | parser.exit() |
|
983 | parser.exit() | |
803 |
|
984 | |||
|
985 | ||||
804 | class _SubParsersAction(Action): |
|
986 | class _SubParsersAction(Action): | |
805 |
|
987 | |||
806 | class _ChoicesPseudoAction(Action): |
|
988 | class _ChoicesPseudoAction(Action): | |
|
989 | ||||
807 | def __init__(self, name, help): |
|
990 | def __init__(self, name, help): | |
808 | sup = super(_SubParsersAction._ChoicesPseudoAction, self) |
|
991 | sup = super(_SubParsersAction._ChoicesPseudoAction, self) | |
809 | sup.__init__(option_strings=[], dest=name, help=help) |
|
992 | sup.__init__(option_strings=[], dest=name, help=help) | |
810 |
|
993 | |||
811 |
|
||||
812 | def __init__(self, |
|
994 | def __init__(self, | |
813 | option_strings, |
|
995 | option_strings, | |
814 | prog, |
|
996 | prog, | |
815 | parser_class, |
|
997 | parser_class, | |
816 | dest=SUPPRESS, |
|
998 | dest=SUPPRESS, | |
817 | help=None, |
|
999 | help=None, | |
818 | metavar=None): |
|
1000 | metavar=None): | |
819 |
|
1001 | |||
820 | self._prog_prefix = prog |
|
1002 | self._prog_prefix = prog | |
821 | self._parser_class = parser_class |
|
1003 | self._parser_class = parser_class | |
822 | self._name_parser_map = {} |
|
1004 | self._name_parser_map = {} | |
823 | self._choices_actions = [] |
|
1005 | self._choices_actions = [] | |
824 |
|
1006 | |||
825 | super(_SubParsersAction, self).__init__( |
|
1007 | super(_SubParsersAction, self).__init__( | |
826 | option_strings=option_strings, |
|
1008 | option_strings=option_strings, | |
827 | dest=dest, |
|
1009 | dest=dest, | |
828 | nargs=PARSER, |
|
1010 | nargs=PARSER, | |
829 | choices=self._name_parser_map, |
|
1011 | choices=self._name_parser_map, | |
830 | help=help, |
|
1012 | help=help, | |
831 | metavar=metavar) |
|
1013 | metavar=metavar) | |
832 |
|
1014 | |||
833 | def add_parser(self, name, **kwargs): |
|
1015 | def add_parser(self, name, **kwargs): | |
834 | # set prog from the existing prefix |
|
1016 | # set prog from the existing prefix | |
835 | if kwargs.get('prog') is None: |
|
1017 | if kwargs.get('prog') is None: | |
836 | kwargs['prog'] = '%s %s' % (self._prog_prefix, name) |
|
1018 | kwargs['prog'] = '%s %s' % (self._prog_prefix, name) | |
837 |
|
1019 | |||
838 | # create a pseudo-action to hold the choice help |
|
1020 | # create a pseudo-action to hold the choice help | |
839 | if 'help' in kwargs: |
|
1021 | if 'help' in kwargs: | |
840 | help = kwargs.pop('help') |
|
1022 | help = kwargs.pop('help') | |
841 | choice_action = self._ChoicesPseudoAction(name, help) |
|
1023 | choice_action = self._ChoicesPseudoAction(name, help) | |
842 | self._choices_actions.append(choice_action) |
|
1024 | self._choices_actions.append(choice_action) | |
843 |
|
1025 | |||
844 | # create the parser and add it to the map |
|
1026 | # create the parser and add it to the map | |
845 | parser = self._parser_class(**kwargs) |
|
1027 | parser = self._parser_class(**kwargs) | |
846 | self._name_parser_map[name] = parser |
|
1028 | self._name_parser_map[name] = parser | |
847 | return parser |
|
1029 | return parser | |
848 |
|
1030 | |||
849 | def _get_subactions(self): |
|
1031 | def _get_subactions(self): | |
850 | return self._choices_actions |
|
1032 | return self._choices_actions | |
851 |
|
1033 | |||
852 | def __call__(self, parser, namespace, values, option_string=None): |
|
1034 | def __call__(self, parser, namespace, values, option_string=None): | |
853 | parser_name = values[0] |
|
1035 | parser_name = values[0] | |
854 | arg_strings = values[1:] |
|
1036 | arg_strings = values[1:] | |
855 |
|
1037 | |||
856 | # set the parser name if requested |
|
1038 | # set the parser name if requested | |
857 | if self.dest is not SUPPRESS: |
|
1039 | if self.dest is not SUPPRESS: | |
858 | setattr(namespace, self.dest, parser_name) |
|
1040 | setattr(namespace, self.dest, parser_name) | |
859 |
|
1041 | |||
860 |
# select the parser |
|
1042 | # select the parser | |
861 | try: |
|
1043 | try: | |
862 | parser = self._name_parser_map[parser_name] |
|
1044 | parser = self._name_parser_map[parser_name] | |
863 | except KeyError: |
|
1045 | except KeyError: | |
864 | tup = parser_name, ', '.join(self._name_parser_map) |
|
1046 | tup = parser_name, ', '.join(self._name_parser_map) | |
865 | msg = _('unknown parser %r (choices: %s)' % tup) |
|
1047 | msg = _('unknown parser %r (choices: %s)' % tup) | |
866 | raise ArgumentError(self, msg) |
|
1048 | raise ArgumentError(self, msg) | |
867 |
|
1049 | |||
868 | # parse all the remaining options into the namespace |
|
1050 | # parse all the remaining options into the namespace | |
869 | parser.parse_args(arg_strings, namespace) |
|
1051 | parser.parse_args(arg_strings, namespace) | |
870 |
|
1052 | |||
871 |
|
1053 | |||
872 | # ============== |
|
1054 | # ============== | |
873 | # Type classes |
|
1055 | # Type classes | |
874 | # ============== |
|
1056 | # ============== | |
875 |
|
1057 | |||
876 | class FileType(object): |
|
1058 | class FileType(object): | |
877 | """Factory for creating file object types |
|
1059 | """Factory for creating file object types | |
878 |
|
1060 | |||
879 | Instances of FileType are typically passed as type= arguments to the |
|
1061 | Instances of FileType are typically passed as type= arguments to the | |
880 | ArgumentParser add_argument() method. |
|
1062 | ArgumentParser add_argument() method. | |
881 |
|
1063 | |||
882 | Keyword Arguments: |
|
1064 | Keyword Arguments: | |
883 | mode -- A string indicating how the file is to be opened. Accepts the |
|
1065 | - mode -- A string indicating how the file is to be opened. Accepts the | |
884 | same values as the builtin open() function. |
|
1066 | same values as the builtin open() function. | |
885 | bufsize -- The file's desired buffer size. Accepts the same values as |
|
1067 | - bufsize -- The file's desired buffer size. Accepts the same values as | |
886 | the builtin open() function. |
|
1068 | the builtin open() function. | |
887 |
""" |
|
1069 | """ | |
|
1070 | ||||
888 | def __init__(self, mode='r', bufsize=None): |
|
1071 | def __init__(self, mode='r', bufsize=None): | |
889 | self._mode = mode |
|
1072 | self._mode = mode | |
890 | self._bufsize = bufsize |
|
1073 | self._bufsize = bufsize | |
891 |
|
1074 | |||
892 | def __call__(self, string): |
|
1075 | def __call__(self, string): | |
893 | # the special argument "-" means sys.std{in,out} |
|
1076 | # the special argument "-" means sys.std{in,out} | |
894 | if string == '-': |
|
1077 | if string == '-': | |
895 |
if self._mode |
|
1078 | if 'r' in self._mode: | |
896 | return _sys.stdin |
|
1079 | return _sys.stdin | |
897 |
elif self._mode |
|
1080 | elif 'w' in self._mode: | |
898 | return _sys.stdout |
|
1081 | return _sys.stdout | |
899 | else: |
|
1082 | else: | |
900 | msg = _('argument "-" with mode %r' % self._mode) |
|
1083 | msg = _('argument "-" with mode %r' % self._mode) | |
901 | raise ValueError(msg) |
|
1084 | raise ValueError(msg) | |
902 |
|
1085 | |||
903 | # all other arguments are used as file names |
|
1086 | # all other arguments are used as file names | |
904 | if self._bufsize: |
|
1087 | if self._bufsize: | |
905 | return open(string, self._mode, self._bufsize) |
|
1088 | return open(string, self._mode, self._bufsize) | |
906 | else: |
|
1089 | else: | |
907 | return open(string, self._mode) |
|
1090 | return open(string, self._mode) | |
908 |
|
1091 | |||
|
1092 | def __repr__(self): | |||
|
1093 | args = [self._mode, self._bufsize] | |||
|
1094 | args_str = ', '.join([repr(arg) for arg in args if arg is not None]) | |||
|
1095 | return '%s(%s)' % (type(self).__name__, args_str) | |||
909 |
|
1096 | |||
910 | # =========================== |
|
1097 | # =========================== | |
911 | # Optional and Positional Parsing |
|
1098 | # Optional and Positional Parsing | |
912 | # =========================== |
|
1099 | # =========================== | |
913 |
|
1100 | |||
914 | class Namespace(_AttributeHolder): |
|
1101 | class Namespace(_AttributeHolder): | |
|
1102 | """Simple object for storing attributes. | |||
|
1103 | ||||
|
1104 | Implements equality by attribute names and values, and provides a simple | |||
|
1105 | string representation. | |||
|
1106 | """ | |||
915 |
|
1107 | |||
916 | def __init__(self, **kwargs): |
|
1108 | def __init__(self, **kwargs): | |
917 |
for name |
|
1109 | for name in kwargs: | |
918 |
setattr(self, name, |
|
1110 | setattr(self, name, kwargs[name]) | |
919 |
|
1111 | |||
920 | def __eq__(self, other): |
|
1112 | def __eq__(self, other): | |
921 | return vars(self) == vars(other) |
|
1113 | return vars(self) == vars(other) | |
922 |
|
1114 | |||
923 | def __ne__(self, other): |
|
1115 | def __ne__(self, other): | |
924 | return not (self == other) |
|
1116 | return not (self == other) | |
925 |
|
1117 | |||
926 |
|
1118 | |||
927 | class _ActionsContainer(object): |
|
1119 | class _ActionsContainer(object): | |
|
1120 | ||||
928 | def __init__(self, |
|
1121 | def __init__(self, | |
929 | description, |
|
1122 | description, | |
930 | prefix_chars, |
|
1123 | prefix_chars, | |
931 | argument_default, |
|
1124 | argument_default, | |
932 | conflict_handler): |
|
1125 | conflict_handler): | |
933 | super(_ActionsContainer, self).__init__() |
|
1126 | super(_ActionsContainer, self).__init__() | |
934 |
|
1127 | |||
935 | self.description = description |
|
1128 | self.description = description | |
936 | self.argument_default = argument_default |
|
1129 | self.argument_default = argument_default | |
937 | self.prefix_chars = prefix_chars |
|
1130 | self.prefix_chars = prefix_chars | |
938 | self.conflict_handler = conflict_handler |
|
1131 | self.conflict_handler = conflict_handler | |
939 |
|
1132 | |||
940 | # set up registries |
|
1133 | # set up registries | |
941 | self._registries = {} |
|
1134 | self._registries = {} | |
942 |
|
1135 | |||
943 | # register actions |
|
1136 | # register actions | |
944 | self.register('action', None, _StoreAction) |
|
1137 | self.register('action', None, _StoreAction) | |
945 | self.register('action', 'store', _StoreAction) |
|
1138 | self.register('action', 'store', _StoreAction) | |
946 | self.register('action', 'store_const', _StoreConstAction) |
|
1139 | self.register('action', 'store_const', _StoreConstAction) | |
947 | self.register('action', 'store_true', _StoreTrueAction) |
|
1140 | self.register('action', 'store_true', _StoreTrueAction) | |
948 | self.register('action', 'store_false', _StoreFalseAction) |
|
1141 | self.register('action', 'store_false', _StoreFalseAction) | |
949 | self.register('action', 'append', _AppendAction) |
|
1142 | self.register('action', 'append', _AppendAction) | |
950 | self.register('action', 'append_const', _AppendConstAction) |
|
1143 | self.register('action', 'append_const', _AppendConstAction) | |
951 | self.register('action', 'count', _CountAction) |
|
1144 | self.register('action', 'count', _CountAction) | |
952 | self.register('action', 'help', _HelpAction) |
|
1145 | self.register('action', 'help', _HelpAction) | |
953 | self.register('action', 'version', _VersionAction) |
|
1146 | self.register('action', 'version', _VersionAction) | |
954 | self.register('action', 'parsers', _SubParsersAction) |
|
1147 | self.register('action', 'parsers', _SubParsersAction) | |
955 |
|
1148 | |||
956 | # raise an exception if the conflict handler is invalid |
|
1149 | # raise an exception if the conflict handler is invalid | |
957 | self._get_handler() |
|
1150 | self._get_handler() | |
958 |
|
1151 | |||
959 | # action storage |
|
1152 | # action storage | |
960 |
self. |
|
1153 | self._actions = [] | |
961 |
self._ |
|
1154 | self._option_string_actions = {} | |
962 | self._positional_actions_full_list = [] |
|
1155 | ||
963 | self._option_strings = {} |
|
1156 | # groups | |
|
1157 | self._action_groups = [] | |||
|
1158 | self._mutually_exclusive_groups = [] | |||
964 |
|
1159 | |||
965 | # defaults storage |
|
1160 | # defaults storage | |
966 | self._defaults = {} |
|
1161 | self._defaults = {} | |
967 |
|
1162 | |||
|
1163 | # determines whether an "option" looks like a negative number | |||
|
1164 | self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$') | |||
|
1165 | ||||
|
1166 | # whether or not there are any optionals that look like negative | |||
|
1167 | # numbers -- uses a list so it can be shared and edited | |||
|
1168 | self._has_negative_number_optionals = [] | |||
|
1169 | ||||
968 | # ==================== |
|
1170 | # ==================== | |
969 | # Registration methods |
|
1171 | # Registration methods | |
970 | # ==================== |
|
1172 | # ==================== | |
971 |
|
||||
972 | def register(self, registry_name, value, object): |
|
1173 | def register(self, registry_name, value, object): | |
973 | registry = self._registries.setdefault(registry_name, {}) |
|
1174 | registry = self._registries.setdefault(registry_name, {}) | |
974 | registry[value] = object |
|
1175 | registry[value] = object | |
975 |
|
1176 | |||
976 | def _registry_get(self, registry_name, value, default=None): |
|
1177 | def _registry_get(self, registry_name, value, default=None): | |
977 | return self._registries[registry_name].get(value, default) |
|
1178 | return self._registries[registry_name].get(value, default) | |
978 |
|
1179 | |||
979 | # ================================== |
|
1180 | # ================================== | |
980 | # Namespace default settings methods |
|
1181 | # Namespace default settings methods | |
981 | # ================================== |
|
1182 | # ================================== | |
982 |
|
||||
983 | def set_defaults(self, **kwargs): |
|
1183 | def set_defaults(self, **kwargs): | |
984 | self._defaults.update(kwargs) |
|
1184 | self._defaults.update(kwargs) | |
985 |
|
1185 | |||
986 | # if these defaults match any existing arguments, replace |
|
1186 | # if these defaults match any existing arguments, replace | |
987 | # the previous default on the object with the new one |
|
1187 | # the previous default on the object with the new one | |
988 |
for action |
|
1188 | for action in self._actions: | |
989 | self._positional_actions_full_list]: |
|
1189 | if action.dest in kwargs: | |
990 |
|
|
1190 | action.default = kwargs[action.dest] | |
991 | if action.dest in kwargs: |
|
|||
992 | action.default = kwargs[action.dest] |
|
|||
993 |
|
1191 | |||
994 | # ======================= |
|
1192 | # ======================= | |
995 | # Adding argument actions |
|
1193 | # Adding argument actions | |
996 | # ======================= |
|
1194 | # ======================= | |
997 |
|
||||
998 | def add_argument(self, *args, **kwargs): |
|
1195 | def add_argument(self, *args, **kwargs): | |
999 | """ |
|
1196 | """ | |
1000 | add_argument(dest, ..., name=value, ...) |
|
1197 | add_argument(dest, ..., name=value, ...) | |
1001 | add_argument(option_string, option_string, ..., name=value, ...) |
|
1198 | add_argument(option_string, option_string, ..., name=value, ...) | |
1002 | """ |
|
1199 | """ | |
1003 |
|
1200 | |||
1004 | # if no positional args are supplied or only one is supplied and |
|
1201 | # if no positional args are supplied or only one is supplied and | |
1005 | # it doesn't look like an option string, parse a positional |
|
1202 | # it doesn't look like an option string, parse a positional | |
1006 | # argument |
|
1203 | # argument | |
1007 | chars = self.prefix_chars |
|
1204 | chars = self.prefix_chars | |
1008 | if not args or len(args) == 1 and args[0][0] not in chars: |
|
1205 | if not args or len(args) == 1 and args[0][0] not in chars: | |
1009 | kwargs = self._get_positional_kwargs(*args, **kwargs) |
|
1206 | kwargs = self._get_positional_kwargs(*args, **kwargs) | |
1010 |
|
1207 | |||
1011 | # otherwise, we're adding an optional argument |
|
1208 | # otherwise, we're adding an optional argument | |
1012 | else: |
|
1209 | else: | |
1013 | kwargs = self._get_optional_kwargs(*args, **kwargs) |
|
1210 | kwargs = self._get_optional_kwargs(*args, **kwargs) | |
1014 |
|
1211 | |||
1015 | # if no default was supplied, use the parser-level default |
|
1212 | # if no default was supplied, use the parser-level default | |
1016 | if 'default' not in kwargs: |
|
1213 | if 'default' not in kwargs: | |
1017 | dest = kwargs['dest'] |
|
1214 | dest = kwargs['dest'] | |
1018 | if dest in self._defaults: |
|
1215 | if dest in self._defaults: | |
1019 | kwargs['default'] = self._defaults[dest] |
|
1216 | kwargs['default'] = self._defaults[dest] | |
1020 | elif self.argument_default is not None: |
|
1217 | elif self.argument_default is not None: | |
1021 | kwargs['default'] = self.argument_default |
|
1218 | kwargs['default'] = self.argument_default | |
1022 |
|
1219 | |||
1023 | # create the action object, and add it to the parser |
|
1220 | # create the action object, and add it to the parser | |
1024 | action_class = self._pop_action_class(kwargs) |
|
1221 | action_class = self._pop_action_class(kwargs) | |
1025 | action = action_class(**kwargs) |
|
1222 | action = action_class(**kwargs) | |
1026 | return self._add_action(action) |
|
1223 | return self._add_action(action) | |
1027 |
|
1224 | |||
|
1225 | def add_argument_group(self, *args, **kwargs): | |||
|
1226 | group = _ArgumentGroup(self, *args, **kwargs) | |||
|
1227 | self._action_groups.append(group) | |||
|
1228 | return group | |||
|
1229 | ||||
|
1230 | def add_mutually_exclusive_group(self, **kwargs): | |||
|
1231 | group = _MutuallyExclusiveGroup(self, **kwargs) | |||
|
1232 | self._mutually_exclusive_groups.append(group) | |||
|
1233 | return group | |||
|
1234 | ||||
1028 | def _add_action(self, action): |
|
1235 | def _add_action(self, action): | |
1029 | # resolve any conflicts |
|
1236 | # resolve any conflicts | |
1030 | self._check_conflict(action) |
|
1237 | self._check_conflict(action) | |
1031 |
|
1238 | |||
1032 |
# add to |
|
1239 | # add to actions list | |
1033 |
|
|
1240 | self._actions.append(action) | |
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 |
|
1241 | action.container = self | |
1039 |
|
1242 | |||
1040 | # index the action by any option strings it has |
|
1243 | # index the action by any option strings it has | |
1041 | for option_string in action.option_strings: |
|
1244 | for option_string in action.option_strings: | |
1042 | self._option_strings[option_string] = action |
|
1245 | self._option_string_actions[option_string] = action | |
|
1246 | ||||
|
1247 | # set the flag if any option strings look like negative numbers | |||
|
1248 | for option_string in action.option_strings: | |||
|
1249 | if self._negative_number_matcher.match(option_string): | |||
|
1250 | if not self._has_negative_number_optionals: | |||
|
1251 | self._has_negative_number_optionals.append(True) | |||
1043 |
|
1252 | |||
1044 | # return the created action |
|
1253 | # return the created action | |
1045 | return action |
|
1254 | return action | |
1046 |
|
1255 | |||
|
1256 | def _remove_action(self, action): | |||
|
1257 | self._actions.remove(action) | |||
|
1258 | ||||
1047 | def _add_container_actions(self, container): |
|
1259 | def _add_container_actions(self, container): | |
1048 | for action in container._optional_actions_list: |
|
1260 | # collect groups by titles | |
1049 | self._add_action(action) |
|
1261 | title_group_map = {} | |
1050 | for action in container._positional_actions_list: |
|
1262 | for group in self._action_groups: | |
1051 | self._add_action(action) |
|
1263 | if group.title in title_group_map: | |
|
1264 | msg = _('cannot merge actions - two groups are named %r') | |||
|
1265 | raise ValueError(msg % (group.title)) | |||
|
1266 | title_group_map[group.title] = group | |||
|
1267 | ||||
|
1268 | # map each action to its group | |||
|
1269 | group_map = {} | |||
|
1270 | for group in container._action_groups: | |||
|
1271 | ||||
|
1272 | # if a group with the title exists, use that, otherwise | |||
|
1273 | # create a new group matching the container's group | |||
|
1274 | if group.title not in title_group_map: | |||
|
1275 | title_group_map[group.title] = self.add_argument_group( | |||
|
1276 | title=group.title, | |||
|
1277 | description=group.description, | |||
|
1278 | conflict_handler=group.conflict_handler) | |||
|
1279 | ||||
|
1280 | # map the actions to their new group | |||
|
1281 | for action in group._group_actions: | |||
|
1282 | group_map[action] = title_group_map[group.title] | |||
|
1283 | ||||
|
1284 | # add all actions to this container or their group | |||
|
1285 | for action in container._actions: | |||
|
1286 | group_map.get(action, self)._add_action(action) | |||
1052 |
|
1287 | |||
1053 | def _get_positional_kwargs(self, dest, **kwargs): |
|
1288 | def _get_positional_kwargs(self, dest, **kwargs): | |
1054 | # make sure required is not specified |
|
1289 | # make sure required is not specified | |
1055 | if 'required' in kwargs: |
|
1290 | if 'required' in kwargs: | |
1056 | msg = _("'required' is an invalid argument for positionals") |
|
1291 | msg = _("'required' is an invalid argument for positionals") | |
1057 | raise TypeError(msg) |
|
1292 | raise TypeError(msg) | |
1058 |
|
1293 | |||
|
1294 | # mark positional arguments as required if at least one is | |||
|
1295 | # always required | |||
|
1296 | if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: | |||
|
1297 | kwargs['required'] = True | |||
|
1298 | if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: | |||
|
1299 | kwargs['required'] = True | |||
|
1300 | ||||
1059 | # return the keyword arguments with no option strings |
|
1301 | # return the keyword arguments with no option strings | |
1060 | return dict(kwargs, dest=dest, option_strings=[]) |
|
1302 | return dict(kwargs, dest=dest, option_strings=[]) | |
1061 |
|
1303 | |||
1062 | def _get_optional_kwargs(self, *args, **kwargs): |
|
1304 | def _get_optional_kwargs(self, *args, **kwargs): | |
1063 | # determine short and long option strings |
|
1305 | # determine short and long option strings | |
1064 | option_strings = [] |
|
1306 | option_strings = [] | |
1065 | long_option_strings = [] |
|
1307 | long_option_strings = [] | |
1066 | for option_string in args: |
|
1308 | for option_string in args: | |
1067 | # error on one-or-fewer-character option strings |
|
1309 | # error on one-or-fewer-character option strings | |
1068 | if len(option_string) < 2: |
|
1310 | if len(option_string) < 2: | |
1069 | msg = _('invalid option string %r: ' |
|
1311 | msg = _('invalid option string %r: ' | |
1070 | 'must be at least two characters long') |
|
1312 | 'must be at least two characters long') | |
1071 | raise ValueError(msg % option_string) |
|
1313 | raise ValueError(msg % option_string) | |
1072 |
|
1314 | |||
1073 | # error on strings that don't start with an appropriate prefix |
|
1315 | # error on strings that don't start with an appropriate prefix | |
1074 | if not option_string[0] in self.prefix_chars: |
|
1316 | if not option_string[0] in self.prefix_chars: | |
1075 | msg = _('invalid option string %r: ' |
|
1317 | msg = _('invalid option string %r: ' | |
1076 | 'must start with a character %r') |
|
1318 | 'must start with a character %r') | |
1077 | tup = option_string, self.prefix_chars |
|
1319 | tup = option_string, self.prefix_chars | |
1078 | raise ValueError(msg % tup) |
|
1320 | raise ValueError(msg % tup) | |
1079 |
|
1321 | |||
1080 | # error on strings that are all prefix characters |
|
1322 | # error on strings that are all prefix characters | |
1081 | if not (set(option_string) - set(self.prefix_chars)): |
|
1323 | if not (_set(option_string) - _set(self.prefix_chars)): | |
1082 | msg = _('invalid option string %r: ' |
|
1324 | msg = _('invalid option string %r: ' | |
1083 | 'must contain characters other than %r') |
|
1325 | 'must contain characters other than %r') | |
1084 | tup = option_string, self.prefix_chars |
|
1326 | tup = option_string, self.prefix_chars | |
1085 | raise ValueError(msg % tup) |
|
1327 | raise ValueError(msg % tup) | |
1086 |
|
1328 | |||
1087 | # strings starting with two prefix characters are long options |
|
1329 | # strings starting with two prefix characters are long options | |
1088 |
option_strings.append(option_string) |
|
1330 | option_strings.append(option_string) | |
1089 | if option_string[0] in self.prefix_chars: |
|
1331 | if option_string[0] in self.prefix_chars: | |
1090 | if option_string[1] in self.prefix_chars: |
|
1332 | if option_string[1] in self.prefix_chars: | |
1091 | long_option_strings.append(option_string) |
|
1333 | long_option_strings.append(option_string) | |
1092 |
|
1334 | |||
1093 | # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' |
|
1335 | # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' | |
1094 | dest = kwargs.pop('dest', None) |
|
1336 | dest = kwargs.pop('dest', None) | |
1095 | if dest is None: |
|
1337 | if dest is None: | |
1096 | if long_option_strings: |
|
1338 | if long_option_strings: | |
1097 | dest_option_string = long_option_strings[0] |
|
1339 | dest_option_string = long_option_strings[0] | |
1098 | else: |
|
1340 | else: | |
1099 | dest_option_string = option_strings[0] |
|
1341 | dest_option_string = option_strings[0] | |
1100 | dest = dest_option_string.lstrip(self.prefix_chars) |
|
1342 | dest = dest_option_string.lstrip(self.prefix_chars) | |
1101 | dest = dest.replace('-', '_') |
|
1343 | dest = dest.replace('-', '_') | |
1102 |
|
1344 | |||
1103 | # return the updated keyword arguments |
|
1345 | # return the updated keyword arguments | |
1104 | return dict(kwargs, dest=dest, option_strings=option_strings) |
|
1346 | return dict(kwargs, dest=dest, option_strings=option_strings) | |
1105 |
|
1347 | |||
1106 | def _pop_action_class(self, kwargs, default=None): |
|
1348 | def _pop_action_class(self, kwargs, default=None): | |
1107 | action = kwargs.pop('action', default) |
|
1349 | action = kwargs.pop('action', default) | |
1108 | return self._registry_get('action', action, action) |
|
1350 | return self._registry_get('action', action, action) | |
1109 |
|
1351 | |||
1110 | def _get_handler(self): |
|
1352 | def _get_handler(self): | |
1111 | # determine function from conflict handler string |
|
1353 | # determine function from conflict handler string | |
1112 | handler_func_name = '_handle_conflict_%s' % self.conflict_handler |
|
1354 | handler_func_name = '_handle_conflict_%s' % self.conflict_handler | |
1113 | try: |
|
1355 | try: | |
1114 | return getattr(self, handler_func_name) |
|
1356 | return getattr(self, handler_func_name) | |
1115 | except AttributeError: |
|
1357 | except AttributeError: | |
1116 | msg = _('invalid conflict_resolution value: %r') |
|
1358 | msg = _('invalid conflict_resolution value: %r') | |
1117 | raise ValueError(msg % self.conflict_handler) |
|
1359 | raise ValueError(msg % self.conflict_handler) | |
1118 |
|
1360 | |||
1119 | def _check_conflict(self, action): |
|
1361 | def _check_conflict(self, action): | |
1120 |
|
1362 | |||
1121 |
# find all options that conflict with this option |
|
1363 | # find all options that conflict with this option | |
1122 | confl_optionals = [] |
|
1364 | confl_optionals = [] | |
1123 | for option_string in action.option_strings: |
|
1365 | for option_string in action.option_strings: | |
1124 | if option_string in self._option_strings: |
|
1366 | if option_string in self._option_string_actions: | |
1125 | confl_optional = self._option_strings[option_string] |
|
1367 | confl_optional = self._option_string_actions[option_string] | |
1126 | confl_optionals.append((option_string, confl_optional)) |
|
1368 | confl_optionals.append((option_string, confl_optional)) | |
1127 |
|
1369 | |||
1128 | # resolve any conflicts |
|
1370 | # resolve any conflicts | |
1129 | if confl_optionals: |
|
1371 | if confl_optionals: | |
1130 | conflict_handler = self._get_handler() |
|
1372 | conflict_handler = self._get_handler() | |
1131 | conflict_handler(action, confl_optionals) |
|
1373 | conflict_handler(action, confl_optionals) | |
1132 |
|
1374 | |||
1133 | def _handle_conflict_error(self, action, conflicting_actions): |
|
1375 | def _handle_conflict_error(self, action, conflicting_actions): | |
1134 | message = _('conflicting option string(s): %s') |
|
1376 | message = _('conflicting option string(s): %s') | |
1135 | conflict_string = ', '.join(option_string |
|
1377 | conflict_string = ', '.join([option_string | |
1136 | for option_string, action |
|
1378 | for option_string, action | |
1137 | in conflicting_actions) |
|
1379 | in conflicting_actions]) | |
1138 | raise ArgumentError(action, message % conflict_string) |
|
1380 | raise ArgumentError(action, message % conflict_string) | |
1139 |
|
1381 | |||
1140 | def _handle_conflict_resolve(self, action, conflicting_actions): |
|
1382 | def _handle_conflict_resolve(self, action, conflicting_actions): | |
1141 |
|
1383 | |||
1142 |
# remove all conflicting options |
|
1384 | # remove all conflicting options | |
1143 | for option_string, action in conflicting_actions: |
|
1385 | for option_string, action in conflicting_actions: | |
1144 |
|
1386 | |||
1145 | # remove the conflicting option |
|
1387 | # remove the conflicting option | |
1146 | action.option_strings.remove(option_string) |
|
1388 | action.option_strings.remove(option_string) | |
1147 | self._option_strings.pop(option_string, None) |
|
1389 | self._option_string_actions.pop(option_string, None) | |
1148 |
|
1390 | |||
1149 | # if the option now has no option string, remove it from the |
|
1391 | # if the option now has no option string, remove it from the | |
1150 | # container holding it |
|
1392 | # container holding it | |
1151 | if not action.option_strings: |
|
1393 | if not action.option_strings: | |
1152 |
action.container._ |
|
1394 | action.container._remove_action(action) | |
1153 |
|
1395 | |||
1154 |
|
1396 | |||
1155 | class _ArgumentGroup(_ActionsContainer): |
|
1397 | class _ArgumentGroup(_ActionsContainer): | |
1156 |
|
1398 | |||
1157 | def __init__(self, container, title=None, description=None, **kwargs): |
|
1399 | def __init__(self, container, title=None, description=None, **kwargs): | |
1158 | # add any missing keyword arguments by checking the container |
|
1400 | # add any missing keyword arguments by checking the container | |
1159 | update = kwargs.setdefault |
|
1401 | update = kwargs.setdefault | |
1160 | update('conflict_handler', container.conflict_handler) |
|
1402 | update('conflict_handler', container.conflict_handler) | |
1161 | update('prefix_chars', container.prefix_chars) |
|
1403 | update('prefix_chars', container.prefix_chars) | |
1162 | update('argument_default', container.argument_default) |
|
1404 | update('argument_default', container.argument_default) | |
1163 | super_init = super(_ArgumentGroup, self).__init__ |
|
1405 | super_init = super(_ArgumentGroup, self).__init__ | |
1164 | super_init(description=description, **kwargs) |
|
1406 | super_init(description=description, **kwargs) | |
1165 |
|
1407 | |||
|
1408 | # group attributes | |||
1166 | self.title = title |
|
1409 | self.title = title | |
|
1410 | self._group_actions = [] | |||
|
1411 | ||||
|
1412 | # share most attributes with the container | |||
1167 | self._registries = container._registries |
|
1413 | self._registries = container._registries | |
1168 |
self. |
|
1414 | self._actions = container._actions | |
1169 | self._option_strings = container._option_strings |
|
1415 | self._option_string_actions = container._option_string_actions | |
1170 | self._defaults = container._defaults |
|
1416 | self._defaults = container._defaults | |
|
1417 | self._has_negative_number_optionals = \ | |||
|
1418 | container._has_negative_number_optionals | |||
|
1419 | ||||
|
1420 | def _add_action(self, action): | |||
|
1421 | action = super(_ArgumentGroup, self)._add_action(action) | |||
|
1422 | self._group_actions.append(action) | |||
|
1423 | return action | |||
|
1424 | ||||
|
1425 | def _remove_action(self, action): | |||
|
1426 | super(_ArgumentGroup, self)._remove_action(action) | |||
|
1427 | self._group_actions.remove(action) | |||
|
1428 | ||||
|
1429 | ||||
|
1430 | class _MutuallyExclusiveGroup(_ArgumentGroup): | |||
|
1431 | ||||
|
1432 | def __init__(self, container, required=False): | |||
|
1433 | super(_MutuallyExclusiveGroup, self).__init__(container) | |||
|
1434 | self.required = required | |||
|
1435 | self._container = container | |||
|
1436 | ||||
|
1437 | def _add_action(self, action): | |||
|
1438 | if action.required: | |||
|
1439 | msg = _('mutually exclusive arguments must be optional') | |||
|
1440 | raise ValueError(msg) | |||
|
1441 | action = self._container._add_action(action) | |||
|
1442 | self._group_actions.append(action) | |||
|
1443 | return action | |||
|
1444 | ||||
|
1445 | def _remove_action(self, action): | |||
|
1446 | self._container._remove_action(action) | |||
|
1447 | self._group_actions.remove(action) | |||
1171 |
|
1448 | |||
1172 |
|
1449 | |||
1173 | class ArgumentParser(_AttributeHolder, _ActionsContainer): |
|
1450 | class ArgumentParser(_AttributeHolder, _ActionsContainer): | |
|
1451 | """Object for parsing command line strings into Python objects. | |||
|
1452 | ||||
|
1453 | Keyword Arguments: | |||
|
1454 | - prog -- The name of the program (default: sys.argv[0]) | |||
|
1455 | - usage -- A usage message (default: auto-generated from arguments) | |||
|
1456 | - description -- A description of what the program does | |||
|
1457 | - epilog -- Text following the argument descriptions | |||
|
1458 | - version -- Add a -v/--version option with the given version string | |||
|
1459 | - parents -- Parsers whose arguments should be copied into this one | |||
|
1460 | - formatter_class -- HelpFormatter class for printing help messages | |||
|
1461 | - prefix_chars -- Characters that prefix optional arguments | |||
|
1462 | - fromfile_prefix_chars -- Characters that prefix files containing | |||
|
1463 | additional arguments | |||
|
1464 | - argument_default -- The default value for all arguments | |||
|
1465 | - conflict_handler -- String indicating how to handle conflicts | |||
|
1466 | - add_help -- Add a -h/-help option | |||
|
1467 | """ | |||
1174 |
|
1468 | |||
1175 | def __init__(self, |
|
1469 | def __init__(self, | |
1176 | prog=None, |
|
1470 | prog=None, | |
1177 | usage=None, |
|
1471 | usage=None, | |
1178 | description=None, |
|
1472 | description=None, | |
1179 | epilog=None, |
|
1473 | epilog=None, | |
1180 | version=None, |
|
1474 | version=None, | |
1181 | parents=[], |
|
1475 | parents=[], | |
1182 | formatter_class=HelpFormatter, |
|
1476 | formatter_class=HelpFormatter, | |
1183 | prefix_chars='-', |
|
1477 | prefix_chars='-', | |
|
1478 | fromfile_prefix_chars=None, | |||
1184 | argument_default=None, |
|
1479 | argument_default=None, | |
1185 | conflict_handler='error', |
|
1480 | conflict_handler='error', | |
1186 | add_help=True): |
|
1481 | add_help=True): | |
1187 |
|
1482 | |||
1188 | superinit = super(ArgumentParser, self).__init__ |
|
1483 | superinit = super(ArgumentParser, self).__init__ | |
1189 | superinit(description=description, |
|
1484 | superinit(description=description, | |
1190 | prefix_chars=prefix_chars, |
|
1485 | prefix_chars=prefix_chars, | |
1191 | argument_default=argument_default, |
|
1486 | argument_default=argument_default, | |
1192 | conflict_handler=conflict_handler) |
|
1487 | conflict_handler=conflict_handler) | |
1193 |
|
1488 | |||
1194 | # default setting for prog |
|
1489 | # default setting for prog | |
1195 | if prog is None: |
|
1490 | if prog is None: | |
1196 | prog = _os.path.basename(_sys.argv[0]) |
|
1491 | prog = _os.path.basename(_sys.argv[0]) | |
1197 |
|
1492 | |||
1198 | self.prog = prog |
|
1493 | self.prog = prog | |
1199 | self.usage = usage |
|
1494 | self.usage = usage | |
1200 | self.epilog = epilog |
|
1495 | self.epilog = epilog | |
1201 | self.version = version |
|
1496 | self.version = version | |
1202 | self.formatter_class = formatter_class |
|
1497 | self.formatter_class = formatter_class | |
|
1498 | self.fromfile_prefix_chars = fromfile_prefix_chars | |||
1203 | self.add_help = add_help |
|
1499 | self.add_help = add_help | |
1204 |
|
1500 | |||
1205 | self._argument_group_class = _ArgumentGroup |
|
1501 | add_group = self.add_argument_group | |
1206 | self._has_subparsers = False |
|
1502 | self._positionals = add_group(_('positional arguments')) | |
1207 | self._argument_groups = [] |
|
1503 | self._optionals = add_group(_('optional arguments')) | |
|
1504 | self._subparsers = None | |||
1208 |
|
1505 | |||
1209 | # register types |
|
1506 | # register types | |
1210 | def identity(string): |
|
1507 | def identity(string): | |
1211 | return string |
|
1508 | return string | |
1212 | self.register('type', None, identity) |
|
1509 | self.register('type', None, identity) | |
1213 |
|
1510 | |||
1214 | # add help and version arguments if necessary |
|
1511 | # add help and version arguments if necessary | |
1215 | # (using explicit default to override global argument_default) |
|
1512 | # (using explicit default to override global argument_default) | |
1216 | if self.add_help: |
|
1513 | if self.add_help: | |
1217 | self.add_argument( |
|
1514 | self.add_argument( | |
1218 | '-h', '--help', action='help', default=SUPPRESS, |
|
1515 | '-h', '--help', action='help', default=SUPPRESS, | |
1219 | help=_('show this help message and exit')) |
|
1516 | help=_('show this help message and exit')) | |
1220 | if self.version: |
|
1517 | if self.version: | |
1221 | self.add_argument( |
|
1518 | self.add_argument( | |
1222 | '-v', '--version', action='version', default=SUPPRESS, |
|
1519 | '-v', '--version', action='version', default=SUPPRESS, | |
1223 | help=_("show program's version number and exit")) |
|
1520 | help=_("show program's version number and exit")) | |
1224 |
|
1521 | |||
1225 | # add parent arguments and defaults |
|
1522 | # add parent arguments and defaults | |
1226 | for parent in parents: |
|
1523 | for parent in parents: | |
1227 | self._add_container_actions(parent) |
|
1524 | self._add_container_actions(parent) | |
1228 | try: |
|
1525 | try: | |
1229 | defaults = parent._defaults |
|
1526 | defaults = parent._defaults | |
1230 | except AttributeError: |
|
1527 | except AttributeError: | |
1231 | pass |
|
1528 | pass | |
1232 | else: |
|
1529 | else: | |
1233 | self._defaults.update(defaults) |
|
1530 | self._defaults.update(defaults) | |
1234 |
|
1531 | |||
1235 | # determines whether an "option" looks like a negative number |
|
|||
1236 | self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$') |
|
|||
1237 |
|
||||
1238 |
|
||||
1239 | # ======================= |
|
1532 | # ======================= | |
1240 | # Pretty __repr__ methods |
|
1533 | # Pretty __repr__ methods | |
1241 | # ======================= |
|
1534 | # ======================= | |
1242 |
|
||||
1243 | def _get_kwargs(self): |
|
1535 | def _get_kwargs(self): | |
1244 | names = [ |
|
1536 | names = [ | |
1245 | 'prog', |
|
1537 | 'prog', | |
1246 | 'usage', |
|
1538 | 'usage', | |
1247 | 'description', |
|
1539 | 'description', | |
1248 | 'version', |
|
1540 | 'version', | |
1249 | 'formatter_class', |
|
1541 | 'formatter_class', | |
1250 | 'conflict_handler', |
|
1542 | 'conflict_handler', | |
1251 | 'add_help', |
|
1543 | 'add_help', | |
1252 | ] |
|
1544 | ] | |
1253 | return [(name, getattr(self, name)) for name in names] |
|
1545 | return [(name, getattr(self, name)) for name in names] | |
1254 |
|
1546 | |||
1255 | # ================================== |
|
1547 | # ================================== | |
1256 | # Optional/Positional adding methods |
|
1548 | # Optional/Positional adding methods | |
1257 | # ================================== |
|
1549 | # ================================== | |
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): |
|
1550 | def add_subparsers(self, **kwargs): | |
1265 |
if self. |
|
1551 | if self._subparsers is not None: | |
1266 | self.error(_('cannot have multiple subparser arguments')) |
|
1552 | self.error(_('cannot have multiple subparser arguments')) | |
1267 |
|
1553 | |||
1268 | # add the parser class to the arguments if it's not present |
|
1554 | # add the parser class to the arguments if it's not present | |
1269 | kwargs.setdefault('parser_class', type(self)) |
|
1555 | kwargs.setdefault('parser_class', type(self)) | |
1270 |
|
1556 | |||
|
1557 | if 'title' in kwargs or 'description' in kwargs: | |||
|
1558 | title = _(kwargs.pop('title', 'subcommands')) | |||
|
1559 | description = _(kwargs.pop('description', None)) | |||
|
1560 | self._subparsers = self.add_argument_group(title, description) | |||
|
1561 | else: | |||
|
1562 | self._subparsers = self._positionals | |||
|
1563 | ||||
1271 | # prog defaults to the usage message of this parser, skipping |
|
1564 | # prog defaults to the usage message of this parser, skipping | |
1272 | # optional arguments and with no "usage:" prefix |
|
1565 | # optional arguments and with no "usage:" prefix | |
1273 | if kwargs.get('prog') is None: |
|
1566 | if kwargs.get('prog') is None: | |
1274 | formatter = self._get_formatter() |
|
1567 | formatter = self._get_formatter() | |
1275 | formatter.add_usage(self.usage, [], |
|
1568 | positionals = self._get_positional_actions() | |
1276 | self._get_positional_actions(), '') |
|
1569 | groups = self._mutually_exclusive_groups | |
|
1570 | formatter.add_usage(self.usage, positionals, groups, '') | |||
1277 | kwargs['prog'] = formatter.format_help().strip() |
|
1571 | kwargs['prog'] = formatter.format_help().strip() | |
1278 |
|
1572 | |||
1279 | # create the parsers action and add it to the positionals list |
|
1573 | # create the parsers action and add it to the positionals list | |
1280 | parsers_class = self._pop_action_class(kwargs, 'parsers') |
|
1574 | parsers_class = self._pop_action_class(kwargs, 'parsers') | |
1281 | action = parsers_class(option_strings=[], **kwargs) |
|
1575 | action = parsers_class(option_strings=[], **kwargs) | |
1282 |
self._ |
|
1576 | self._subparsers._add_action(action) | |
1283 | self._positional_actions_full_list.append(action) |
|
|||
1284 | self._has_subparsers = True |
|
|||
1285 |
|
1577 | |||
1286 | # return the created parsers action |
|
1578 | # return the created parsers action | |
1287 | return action |
|
1579 | return action | |
1288 |
|
1580 | |||
1289 |
def _add_ |
|
1581 | def _add_action(self, action): | |
1290 | super(ArgumentParser, self)._add_container_actions(container) |
|
1582 | if action.option_strings: | |
1291 | try: |
|
1583 | self._optionals._add_action(action) | |
1292 | groups = container._argument_groups |
|
|||
1293 | except AttributeError: |
|
|||
1294 | pass |
|
|||
1295 | else: |
|
1584 | else: | |
1296 | for group in groups: |
|
1585 | self._positionals._add_action(action) | |
1297 | new_group = self.add_argument_group( |
|
1586 | return action | |
1298 | title=group.title, |
|
|||
1299 | description=group.description, |
|
|||
1300 | conflict_handler=group.conflict_handler) |
|
|||
1301 | new_group._add_container_actions(group) |
|
|||
1302 |
|
1587 | |||
1303 | def _get_optional_actions(self): |
|
1588 | def _get_optional_actions(self): | |
1304 |
|
|
1589 | return [action | |
1305 | actions.extend(self._optional_actions_list) |
|
1590 | for action in self._actions | |
1306 | for argument_group in self._argument_groups: |
|
1591 | if action.option_strings] | |
1307 | actions.extend(argument_group._optional_actions_list) |
|
|||
1308 | return actions |
|
|||
1309 |
|
1592 | |||
1310 | def _get_positional_actions(self): |
|
1593 | def _get_positional_actions(self): | |
1311 | return list(self._positional_actions_full_list) |
|
1594 | return [action | |
1312 |
|
1595 | for action in self._actions | ||
|
1596 | if not action.option_strings] | |||
1313 |
|
1597 | |||
1314 | # ===================================== |
|
1598 | # ===================================== | |
1315 | # Command line argument parsing methods |
|
1599 | # Command line argument parsing methods | |
1316 | # ===================================== |
|
1600 | # ===================================== | |
1317 |
|
||||
1318 | def parse_args(self, args=None, namespace=None): |
|
1601 | def parse_args(self, args=None, namespace=None): | |
|
1602 | args, argv = self.parse_known_args(args, namespace) | |||
|
1603 | if argv: | |||
|
1604 | msg = _('unrecognized arguments: %s') | |||
|
1605 | self.error(msg % ' '.join(argv)) | |||
|
1606 | return args | |||
|
1607 | ||||
|
1608 | def parse_known_args(self, args=None, namespace=None): | |||
1319 | # args default to the system args |
|
1609 | # args default to the system args | |
1320 | if args is None: |
|
1610 | if args is None: | |
1321 | args = _sys.argv[1:] |
|
1611 | args = _sys.argv[1:] | |
1322 |
|
1612 | |||
1323 | # default Namespace built from parser defaults |
|
1613 | # default Namespace built from parser defaults | |
1324 | if namespace is None: |
|
1614 | if namespace is None: | |
1325 | namespace = Namespace() |
|
1615 | namespace = Namespace() | |
1326 |
|
1616 | |||
1327 | # add any action defaults that aren't present |
|
1617 | # add any action defaults that aren't present | |
1328 | optional_actions = self._get_optional_actions() |
|
1618 | for action in self._actions: | |
1329 | positional_actions = self._get_positional_actions() |
|
|||
1330 | for action in optional_actions + positional_actions: |
|
|||
1331 | if action.dest is not SUPPRESS: |
|
1619 | if action.dest is not SUPPRESS: | |
1332 | if not hasattr(namespace, action.dest): |
|
1620 | if not hasattr(namespace, action.dest): | |
1333 | if action.default is not SUPPRESS: |
|
1621 | if action.default is not SUPPRESS: | |
1334 | default = action.default |
|
1622 | default = action.default | |
1335 | if isinstance(action.default, basestring): |
|
1623 | if isinstance(action.default, _basestring): | |
1336 | default = self._get_value(action, default) |
|
1624 | default = self._get_value(action, default) | |
1337 | setattr(namespace, action.dest, default) |
|
1625 | setattr(namespace, action.dest, default) | |
1338 |
|
1626 | |||
1339 | # add any parser defaults that aren't present |
|
1627 | # add any parser defaults that aren't present | |
1340 |
for dest |
|
1628 | for dest in self._defaults: | |
1341 | if not hasattr(namespace, dest): |
|
1629 | if not hasattr(namespace, dest): | |
1342 |
setattr(namespace, dest, |
|
1630 | setattr(namespace, dest, self._defaults[dest]) | |
1343 |
|
1631 | |||
1344 | # parse the arguments and exit if there are any errors |
|
1632 | # parse the arguments and exit if there are any errors | |
1345 | try: |
|
1633 | try: | |
1346 |
re |
|
1634 | return self._parse_known_args(args, namespace) | |
1347 |
except ArgumentError |
|
1635 | except ArgumentError: | |
|
1636 | err = _sys.exc_info()[1] | |||
1348 | self.error(str(err)) |
|
1637 | self.error(str(err)) | |
1349 |
|
1638 | |||
1350 | # make sure all required optionals are present |
|
1639 | def _parse_known_args(self, arg_strings, namespace): | |
1351 | for action in self._get_optional_actions(): |
|
1640 | # replace arg strings that are file references | |
1352 | if action.required: |
|
1641 | if self.fromfile_prefix_chars is not None: | |
1353 | if getattr(result, action.dest, None) is None: |
|
1642 | arg_strings = self._read_args_from_files(arg_strings) | |
1354 | opt_strs = '/'.join(action.option_strings) |
|
1643 | ||
1355 | msg = _('option %s is required' % opt_strs) |
|
1644 | # map all mutually exclusive arguments to the other arguments | |
1356 | self.error(msg) |
|
1645 | # they can't occur with | |
1357 |
|
1646 | action_conflicts = {} | ||
1358 | # return the parsed arguments |
|
1647 | for mutex_group in self._mutually_exclusive_groups: | |
1359 | return result |
|
1648 | group_actions = mutex_group._group_actions | |
1360 |
|
1649 | for i, mutex_action in enumerate(mutex_group._group_actions): | ||
1361 | def _parse_args(self, arg_strings, namespace): |
|
1650 | conflicts = action_conflicts.setdefault(mutex_action, []) | |
|
1651 | conflicts.extend(group_actions[:i]) | |||
|
1652 | conflicts.extend(group_actions[i + 1:]) | |||
1362 |
|
1653 | |||
1363 | # find all option indices, and determine the arg_string_pattern |
|
1654 | # find all option indices, and determine the arg_string_pattern | |
1364 | # which has an 'O' if there is an option at an index, |
|
1655 | # 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 '--' |
|
1656 | # an 'A' if there is an argument, or a '-' if there is a '--' | |
1366 | option_string_indices = {} |
|
1657 | option_string_indices = {} | |
1367 | arg_string_pattern_parts = [] |
|
1658 | arg_string_pattern_parts = [] | |
1368 | arg_strings_iter = iter(arg_strings) |
|
1659 | arg_strings_iter = iter(arg_strings) | |
1369 | for i, arg_string in enumerate(arg_strings_iter): |
|
1660 | for i, arg_string in enumerate(arg_strings_iter): | |
1370 |
|
1661 | |||
1371 | # all args after -- are non-options |
|
1662 | # all args after -- are non-options | |
1372 | if arg_string == '--': |
|
1663 | if arg_string == '--': | |
1373 | arg_string_pattern_parts.append('-') |
|
1664 | arg_string_pattern_parts.append('-') | |
1374 | for arg_string in arg_strings_iter: |
|
1665 | for arg_string in arg_strings_iter: | |
1375 | arg_string_pattern_parts.append('A') |
|
1666 | arg_string_pattern_parts.append('A') | |
1376 |
|
1667 | |||
1377 | # otherwise, add the arg to the arg strings |
|
1668 | # otherwise, add the arg to the arg strings | |
1378 | # and note the index if it was an option |
|
1669 | # and note the index if it was an option | |
1379 | else: |
|
1670 | else: | |
1380 | option_tuple = self._parse_optional(arg_string) |
|
1671 | option_tuple = self._parse_optional(arg_string) | |
1381 | if option_tuple is None: |
|
1672 | if option_tuple is None: | |
1382 | pattern = 'A' |
|
1673 | pattern = 'A' | |
1383 | else: |
|
1674 | else: | |
1384 | option_string_indices[i] = option_tuple |
|
1675 | option_string_indices[i] = option_tuple | |
1385 | pattern = 'O' |
|
1676 | pattern = 'O' | |
1386 | arg_string_pattern_parts.append(pattern) |
|
1677 | arg_string_pattern_parts.append(pattern) | |
1387 |
|
1678 | |||
1388 | # join the pieces together to form the pattern |
|
1679 | # join the pieces together to form the pattern | |
1389 | arg_strings_pattern = ''.join(arg_string_pattern_parts) |
|
1680 | arg_strings_pattern = ''.join(arg_string_pattern_parts) | |
1390 |
|
1681 | |||
1391 | # converts arg strings to the appropriate and then takes the action |
|
1682 | # converts arg strings to the appropriate and then takes the action | |
|
1683 | seen_actions = _set() | |||
|
1684 | seen_non_default_actions = _set() | |||
|
1685 | ||||
1392 | def take_action(action, argument_strings, option_string=None): |
|
1686 | def take_action(action, argument_strings, option_string=None): | |
|
1687 | seen_actions.add(action) | |||
1393 | argument_values = self._get_values(action, argument_strings) |
|
1688 | argument_values = self._get_values(action, argument_strings) | |
|
1689 | ||||
|
1690 | # error if this argument is not allowed with other previously | |||
|
1691 | # seen arguments, assuming that actions that use the default | |||
|
1692 | # value don't really count as "present" | |||
|
1693 | if argument_values is not action.default: | |||
|
1694 | seen_non_default_actions.add(action) | |||
|
1695 | for conflict_action in action_conflicts.get(action, []): | |||
|
1696 | if conflict_action in seen_non_default_actions: | |||
|
1697 | msg = _('not allowed with argument %s') | |||
|
1698 | action_name = _get_action_name(conflict_action) | |||
|
1699 | raise ArgumentError(action, msg % action_name) | |||
|
1700 | ||||
1394 | # take the action if we didn't receive a SUPPRESS value |
|
1701 | # take the action if we didn't receive a SUPPRESS value | |
1395 |
# (e.g. from a default) |
|
1702 | # (e.g. from a default) | |
1396 | if argument_values is not SUPPRESS: |
|
1703 | if argument_values is not SUPPRESS: | |
1397 | action(self, namespace, argument_values, option_string) |
|
1704 | action(self, namespace, argument_values, option_string) | |
1398 |
|
1705 | |||
1399 | # function to convert arg_strings into an optional action |
|
1706 | # function to convert arg_strings into an optional action | |
1400 | def consume_optional(start_index): |
|
1707 | def consume_optional(start_index): | |
1401 |
|
1708 | |||
1402 |
# |
|
1709 | # get the optional identified at this index | |
1403 | # argument out of the option string |
|
|||
1404 | option_tuple = option_string_indices[start_index] |
|
1710 | option_tuple = option_string_indices[start_index] | |
1405 | action, option_string, explicit_arg = option_tuple |
|
1711 | action, option_string, explicit_arg = option_tuple | |
1406 |
|
1712 | |||
1407 | # loop because single-dash options can be chained |
|
1713 | # identify additional optionals in the same arg string | |
1408 | # (e.g. -xyz is the same as -x -y -z if no args are required) |
|
1714 | # (e.g. -xyz is the same as -x -y -z if no args are required) | |
1409 | match_argument = self._match_argument |
|
1715 | match_argument = self._match_argument | |
1410 | action_tuples = [] |
|
1716 | action_tuples = [] | |
1411 | while True: |
|
1717 | while True: | |
1412 |
|
1718 | |||
1413 |
# if we found no optional action, |
|
1719 | # if we found no optional action, skip it | |
1414 | if action is None: |
|
1720 | if action is None: | |
1415 | self.error(_('no such option: %s') % option_string) |
|
1721 | extras.append(arg_strings[start_index]) | |
|
1722 | return start_index + 1 | |||
1416 |
|
1723 | |||
1417 | # if there is an explicit argument, try to match the |
|
1724 | # if there is an explicit argument, try to match the | |
1418 | # optional's string arguments to only this |
|
1725 | # optional's string arguments to only this | |
1419 | if explicit_arg is not None: |
|
1726 | if explicit_arg is not None: | |
1420 | arg_count = match_argument(action, 'A') |
|
1727 | arg_count = match_argument(action, 'A') | |
1421 |
|
1728 | |||
1422 | # if the action is a single-dash option and takes no |
|
1729 | # if the action is a single-dash option and takes no | |
1423 | # arguments, try to parse more single-dash options out |
|
1730 | # arguments, try to parse more single-dash options out | |
1424 | # of the tail of the option string |
|
1731 | # of the tail of the option string | |
1425 | chars = self.prefix_chars |
|
1732 | chars = self.prefix_chars | |
1426 | if arg_count == 0 and option_string[1] not in chars: |
|
1733 | if arg_count == 0 and option_string[1] not in chars: | |
1427 | action_tuples.append((action, [], option_string)) |
|
1734 | action_tuples.append((action, [], option_string)) | |
1428 | parse_optional = self._parse_optional |
|
|||
1429 | for char in self.prefix_chars: |
|
1735 | for char in self.prefix_chars: | |
1430 | option_string = char + explicit_arg |
|
1736 | option_string = char + explicit_arg[0] | |
1431 | option_tuple = parse_optional(option_string) |
|
1737 | explicit_arg = explicit_arg[1:] or None | |
1432 |
|
|
1738 | optionals_map = self._option_string_actions | |
|
1739 | if option_string in optionals_map: | |||
|
1740 | action = optionals_map[option_string] | |||
1433 | break |
|
1741 | break | |
1434 | else: |
|
1742 | else: | |
1435 | msg = _('ignored explicit argument %r') |
|
1743 | msg = _('ignored explicit argument %r') | |
1436 | raise ArgumentError(action, msg % explicit_arg) |
|
1744 | raise ArgumentError(action, msg % explicit_arg) | |
1437 |
|
1745 | |||
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 |
|
1746 | # if the action expect exactly one argument, we've | |
1442 | # successfully matched the option; exit the loop |
|
1747 | # successfully matched the option; exit the loop | |
1443 | elif arg_count == 1: |
|
1748 | elif arg_count == 1: | |
1444 | stop = start_index + 1 |
|
1749 | stop = start_index + 1 | |
1445 | args = [explicit_arg] |
|
1750 | args = [explicit_arg] | |
1446 | action_tuples.append((action, args, option_string)) |
|
1751 | action_tuples.append((action, args, option_string)) | |
1447 | break |
|
1752 | break | |
1448 |
|
1753 | |||
1449 | # error if a double-dash option did not use the |
|
1754 | # error if a double-dash option did not use the | |
1450 | # explicit argument |
|
1755 | # explicit argument | |
1451 | else: |
|
1756 | else: | |
1452 | msg = _('ignored explicit argument %r') |
|
1757 | msg = _('ignored explicit argument %r') | |
1453 | raise ArgumentError(action, msg % explicit_arg) |
|
1758 | raise ArgumentError(action, msg % explicit_arg) | |
1454 |
|
1759 | |||
1455 | # if there is no explicit argument, try to match the |
|
1760 | # if there is no explicit argument, try to match the | |
1456 | # optional's string arguments with the following strings |
|
1761 | # optional's string arguments with the following strings | |
1457 | # if successful, exit the loop |
|
1762 | # if successful, exit the loop | |
1458 | else: |
|
1763 | else: | |
1459 | start = start_index + 1 |
|
1764 | start = start_index + 1 | |
1460 | selected_patterns = arg_strings_pattern[start:] |
|
1765 | selected_patterns = arg_strings_pattern[start:] | |
1461 | arg_count = match_argument(action, selected_patterns) |
|
1766 | arg_count = match_argument(action, selected_patterns) | |
1462 | stop = start + arg_count |
|
1767 | stop = start + arg_count | |
1463 | args = arg_strings[start:stop] |
|
1768 | args = arg_strings[start:stop] | |
1464 | action_tuples.append((action, args, option_string)) |
|
1769 | action_tuples.append((action, args, option_string)) | |
1465 | break |
|
1770 | break | |
1466 |
|
1771 | |||
1467 | # add the Optional to the list and return the index at which |
|
1772 | # add the Optional to the list and return the index at which | |
1468 | # the Optional's string args stopped |
|
1773 | # the Optional's string args stopped | |
1469 | assert action_tuples |
|
1774 | assert action_tuples | |
1470 | for action, args, option_string in action_tuples: |
|
1775 | for action, args, option_string in action_tuples: | |
1471 | take_action(action, args, option_string) |
|
1776 | take_action(action, args, option_string) | |
1472 | return stop |
|
1777 | return stop | |
1473 |
|
1778 | |||
1474 | # the list of Positionals left to be parsed; this is modified |
|
1779 | # the list of Positionals left to be parsed; this is modified | |
1475 | # by consume_positionals() |
|
1780 | # by consume_positionals() | |
1476 | positionals = self._get_positional_actions() |
|
1781 | positionals = self._get_positional_actions() | |
1477 |
|
1782 | |||
1478 | # function to convert arg_strings into positional actions |
|
1783 | # function to convert arg_strings into positional actions | |
1479 | def consume_positionals(start_index): |
|
1784 | def consume_positionals(start_index): | |
1480 | # match as many Positionals as possible |
|
1785 | # match as many Positionals as possible | |
1481 | match_partial = self._match_arguments_partial |
|
1786 | match_partial = self._match_arguments_partial | |
1482 | selected_pattern = arg_strings_pattern[start_index:] |
|
1787 | selected_pattern = arg_strings_pattern[start_index:] | |
1483 | arg_counts = match_partial(positionals, selected_pattern) |
|
1788 | arg_counts = match_partial(positionals, selected_pattern) | |
1484 |
|
1789 | |||
1485 | # slice off the appropriate arg strings for each Positional |
|
1790 | # slice off the appropriate arg strings for each Positional | |
1486 | # and add the Positional and its args to the list |
|
1791 | # and add the Positional and its args to the list | |
1487 | for action, arg_count in zip(positionals, arg_counts): |
|
1792 | for action, arg_count in zip(positionals, arg_counts): | |
1488 | args = arg_strings[start_index: start_index + arg_count] |
|
1793 | args = arg_strings[start_index: start_index + arg_count] | |
1489 | start_index += arg_count |
|
1794 | start_index += arg_count | |
1490 | take_action(action, args) |
|
1795 | take_action(action, args) | |
1491 |
|
1796 | |||
1492 | # slice off the Positionals that we just parsed and return the |
|
1797 | # slice off the Positionals that we just parsed and return the | |
1493 | # index at which the Positionals' string args stopped |
|
1798 | # index at which the Positionals' string args stopped | |
1494 | positionals[:] = positionals[len(arg_counts):] |
|
1799 | positionals[:] = positionals[len(arg_counts):] | |
1495 | return start_index |
|
1800 | return start_index | |
1496 |
|
1801 | |||
1497 | # consume Positionals and Optionals alternately, until we have |
|
1802 | # consume Positionals and Optionals alternately, until we have | |
1498 | # passed the last option string |
|
1803 | # passed the last option string | |
|
1804 | extras = [] | |||
1499 | start_index = 0 |
|
1805 | start_index = 0 | |
1500 | if option_string_indices: |
|
1806 | if option_string_indices: | |
1501 | max_option_string_index = max(option_string_indices) |
|
1807 | max_option_string_index = max(option_string_indices) | |
1502 | else: |
|
1808 | else: | |
1503 | max_option_string_index = -1 |
|
1809 | max_option_string_index = -1 | |
1504 | while start_index <= max_option_string_index: |
|
1810 | while start_index <= max_option_string_index: | |
1505 |
|
1811 | |||
1506 | # consume any Positionals preceding the next option |
|
1812 | # consume any Positionals preceding the next option | |
1507 | next_option_string_index = min( |
|
1813 | next_option_string_index = min([ | |
1508 | index |
|
1814 | index | |
1509 | for index in option_string_indices |
|
1815 | for index in option_string_indices | |
1510 | if index >= start_index) |
|
1816 | if index >= start_index]) | |
1511 | if start_index != next_option_string_index: |
|
1817 | if start_index != next_option_string_index: | |
1512 | positionals_end_index = consume_positionals(start_index) |
|
1818 | positionals_end_index = consume_positionals(start_index) | |
1513 |
|
1819 | |||
1514 | # only try to parse the next optional if we didn't consume |
|
1820 | # only try to parse the next optional if we didn't consume | |
1515 | # the option string during the positionals parsing |
|
1821 | # the option string during the positionals parsing | |
1516 | if positionals_end_index > start_index: |
|
1822 | if positionals_end_index > start_index: | |
1517 | start_index = positionals_end_index |
|
1823 | start_index = positionals_end_index | |
1518 | continue |
|
1824 | continue | |
1519 | else: |
|
1825 | else: | |
1520 | start_index = positionals_end_index |
|
1826 | start_index = positionals_end_index | |
1521 |
|
1827 | |||
1522 | # if we consumed all the positionals we could and we're not |
|
1828 | # if we consumed all the positionals we could and we're not | |
1523 |
# at the index of an option string, there were |
|
1829 | # at the index of an option string, there were extra arguments | |
1524 | # arguments |
|
|||
1525 | if start_index not in option_string_indices: |
|
1830 | if start_index not in option_string_indices: | |
1526 | msg = _('extra arguments found: %s') |
|
1831 | strings = arg_strings[start_index:next_option_string_index] | |
1527 | extras = arg_strings[start_index:next_option_string_index] |
|
1832 | extras.extend(strings) | |
1528 | self.error(msg % ' '.join(extras)) |
|
1833 | start_index = next_option_string_index | |
1529 |
|
1834 | |||
1530 | # consume the next optional and any arguments for it |
|
1835 | # consume the next optional and any arguments for it | |
1531 | start_index = consume_optional(start_index) |
|
1836 | start_index = consume_optional(start_index) | |
1532 |
|
1837 | |||
1533 | # consume any positionals following the last Optional |
|
1838 | # consume any positionals following the last Optional | |
1534 | stop_index = consume_positionals(start_index) |
|
1839 | stop_index = consume_positionals(start_index) | |
1535 |
|
1840 | |||
1536 |
# if we didn't consume all the argument strings, there were |
|
1841 | # if we didn't consume all the argument strings, there were extras | |
1537 | # many supplied |
|
1842 | extras.extend(arg_strings[stop_index:]) | |
1538 | if stop_index != len(arg_strings): |
|
|||
1539 | extras = arg_strings[stop_index:] |
|
|||
1540 | self.error(_('extra arguments found: %s') % ' '.join(extras)) |
|
|||
1541 |
|
1843 | |||
1542 | # if we didn't use all the Positional objects, there were too few |
|
1844 | # if we didn't use all the Positional objects, there were too few | |
1543 | # arg strings supplied. |
|
1845 | # arg strings supplied. | |
1544 | if positionals: |
|
1846 | if positionals: | |
1545 | self.error(_('too few arguments')) |
|
1847 | self.error(_('too few arguments')) | |
1546 |
|
1848 | |||
1547 | # return the updated namespace |
|
1849 | # make sure all required actions were present | |
1548 | return namespace |
|
1850 | for action in self._actions: | |
|
1851 | if action.required: | |||
|
1852 | if action not in seen_actions: | |||
|
1853 | name = _get_action_name(action) | |||
|
1854 | self.error(_('argument %s is required') % name) | |||
|
1855 | ||||
|
1856 | # make sure all required groups had one option present | |||
|
1857 | for group in self._mutually_exclusive_groups: | |||
|
1858 | if group.required: | |||
|
1859 | for action in group._group_actions: | |||
|
1860 | if action in seen_non_default_actions: | |||
|
1861 | break | |||
|
1862 | ||||
|
1863 | # if no actions were used, report the error | |||
|
1864 | else: | |||
|
1865 | names = [_get_action_name(action) | |||
|
1866 | for action in group._group_actions | |||
|
1867 | if action.help is not SUPPRESS] | |||
|
1868 | msg = _('one of the arguments %s is required') | |||
|
1869 | self.error(msg % ' '.join(names)) | |||
|
1870 | ||||
|
1871 | # return the updated namespace and the extra arguments | |||
|
1872 | return namespace, extras | |||
|
1873 | ||||
|
1874 | def _read_args_from_files(self, arg_strings): | |||
|
1875 | # expand arguments referencing files | |||
|
1876 | new_arg_strings = [] | |||
|
1877 | for arg_string in arg_strings: | |||
|
1878 | ||||
|
1879 | # for regular arguments, just add them back into the list | |||
|
1880 | if arg_string[0] not in self.fromfile_prefix_chars: | |||
|
1881 | new_arg_strings.append(arg_string) | |||
|
1882 | ||||
|
1883 | # replace arguments referencing files with the file content | |||
|
1884 | else: | |||
|
1885 | try: | |||
|
1886 | args_file = open(arg_string[1:]) | |||
|
1887 | try: | |||
|
1888 | arg_strings = args_file.read().splitlines() | |||
|
1889 | arg_strings = self._read_args_from_files(arg_strings) | |||
|
1890 | new_arg_strings.extend(arg_strings) | |||
|
1891 | finally: | |||
|
1892 | args_file.close() | |||
|
1893 | except IOError: | |||
|
1894 | err = _sys.exc_info()[1] | |||
|
1895 | self.error(str(err)) | |||
|
1896 | ||||
|
1897 | # return the modified argument list | |||
|
1898 | return new_arg_strings | |||
1549 |
|
1899 | |||
1550 | def _match_argument(self, action, arg_strings_pattern): |
|
1900 | def _match_argument(self, action, arg_strings_pattern): | |
1551 | # match the pattern for this action to the arg strings |
|
1901 | # match the pattern for this action to the arg strings | |
1552 | nargs_pattern = self._get_nargs_pattern(action) |
|
1902 | nargs_pattern = self._get_nargs_pattern(action) | |
1553 | match = _re.match(nargs_pattern, arg_strings_pattern) |
|
1903 | match = _re.match(nargs_pattern, arg_strings_pattern) | |
1554 |
|
1904 | |||
1555 |
# raise an exception if we weren't able to find a match |
|
1905 | # raise an exception if we weren't able to find a match | |
1556 | if match is None: |
|
1906 | if match is None: | |
1557 | nargs_errors = { |
|
1907 | nargs_errors = { | |
1558 | None:_('expected one argument'), |
|
1908 | None: _('expected one argument'), | |
1559 | OPTIONAL:_('expected at most one argument'), |
|
1909 | OPTIONAL: _('expected at most one argument'), | |
1560 | ONE_OR_MORE:_('expected at least one argument') |
|
1910 | ONE_OR_MORE: _('expected at least one argument'), | |
1561 | } |
|
1911 | } | |
1562 | default = _('expected %s argument(s)') % action.nargs |
|
1912 | default = _('expected %s argument(s)') % action.nargs | |
1563 | msg = nargs_errors.get(action.nargs, default) |
|
1913 | msg = nargs_errors.get(action.nargs, default) | |
1564 | raise ArgumentError(action, msg) |
|
1914 | raise ArgumentError(action, msg) | |
1565 |
|
1915 | |||
1566 | # return the number of arguments matched |
|
1916 | # return the number of arguments matched | |
1567 | return len(match.group(1)) |
|
1917 | return len(match.group(1)) | |
1568 |
|
1918 | |||
1569 | def _match_arguments_partial(self, actions, arg_strings_pattern): |
|
1919 | def _match_arguments_partial(self, actions, arg_strings_pattern): | |
1570 | # progressively shorten the actions list by slicing off the |
|
1920 | # progressively shorten the actions list by slicing off the | |
1571 | # final actions until we find a match |
|
1921 | # final actions until we find a match | |
1572 | result = [] |
|
1922 | result = [] | |
1573 |
for i in |
|
1923 | for i in range(len(actions), 0, -1): | |
1574 | actions_slice = actions[:i] |
|
1924 | actions_slice = actions[:i] | |
1575 | pattern = ''.join(self._get_nargs_pattern(action) |
|
1925 | pattern = ''.join([self._get_nargs_pattern(action) | |
1576 | for action in actions_slice) |
|
1926 | for action in actions_slice]) | |
1577 | match = _re.match(pattern, arg_strings_pattern) |
|
1927 | match = _re.match(pattern, arg_strings_pattern) | |
1578 | if match is not None: |
|
1928 | if match is not None: | |
1579 | result.extend(len(string) for string in match.groups()) |
|
1929 | result.extend([len(string) for string in match.groups()]) | |
1580 | break |
|
1930 | break | |
1581 |
|
1931 | |||
1582 | # return the list of arg string counts |
|
1932 | # return the list of arg string counts | |
1583 | return result |
|
1933 | return result | |
1584 |
|
1934 | |||
1585 | def _parse_optional(self, arg_string): |
|
1935 | def _parse_optional(self, arg_string): | |
|
1936 | # if it's an empty string, it was meant to be a positional | |||
|
1937 | if not arg_string: | |||
|
1938 | return None | |||
|
1939 | ||||
1586 | # if it doesn't start with a prefix, it was meant to be positional |
|
1940 | # if it doesn't start with a prefix, it was meant to be positional | |
1587 | if not arg_string[0] in self.prefix_chars: |
|
1941 | if not arg_string[0] in self.prefix_chars: | |
1588 | return None |
|
1942 | return None | |
1589 |
|
1943 | |||
1590 | # if it's just dashes, it was meant to be positional |
|
1944 | # if it's just dashes, it was meant to be positional | |
1591 | if not arg_string.strip('-'): |
|
1945 | if not arg_string.strip('-'): | |
1592 | return None |
|
1946 | return None | |
1593 |
|
1947 | |||
1594 | # if the option string is present in the parser, return the action |
|
1948 | # if the option string is present in the parser, return the action | |
1595 | if arg_string in self._option_strings: |
|
1949 | if arg_string in self._option_string_actions: | |
1596 | action = self._option_strings[arg_string] |
|
1950 | action = self._option_string_actions[arg_string] | |
1597 | return action, arg_string, None |
|
1951 | return action, arg_string, None | |
1598 |
|
1952 | |||
1599 | # search through all possible prefixes of the option string |
|
1953 | # search through all possible prefixes of the option string | |
1600 | # and all actions in the parser for possible interpretations |
|
1954 | # and all actions in the parser for possible interpretations | |
1601 | option_tuples = [] |
|
1955 | option_tuples = self._get_option_tuples(arg_string) | |
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 |
|
1956 | |||
1611 | # if multiple actions match, the option string was ambiguous |
|
1957 | # if multiple actions match, the option string was ambiguous | |
1612 | if len(option_tuples) > 1: |
|
1958 | if len(option_tuples) > 1: | |
1613 |
options = ', '.join( |
|
1959 | options = ', '.join([option_string | |
|
1960 | for action, option_string, explicit_arg in option_tuples]) | |||
1614 | tup = arg_string, options |
|
1961 | tup = arg_string, options | |
1615 | self.error(_('ambiguous option: %s could match %s') % tup) |
|
1962 | self.error(_('ambiguous option: %s could match %s') % tup) | |
1616 |
|
1963 | |||
1617 | # if exactly one action matched, this segmentation is good, |
|
1964 | # if exactly one action matched, this segmentation is good, | |
1618 | # so return the parsed action |
|
1965 | # so return the parsed action | |
1619 | elif len(option_tuples) == 1: |
|
1966 | elif len(option_tuples) == 1: | |
1620 | option_tuple, = option_tuples |
|
1967 | option_tuple, = option_tuples | |
1621 | return option_tuple |
|
1968 | return option_tuple | |
1622 |
|
1969 | |||
1623 | # if it was not found as an option, but it looks like a negative |
|
1970 | # if it was not found as an option, but it looks like a negative | |
1624 | # number, it was meant to be positional |
|
1971 | # number, it was meant to be positional | |
|
1972 | # unless there are negative-number-like options | |||
1625 | if self._negative_number_matcher.match(arg_string): |
|
1973 | if self._negative_number_matcher.match(arg_string): | |
|
1974 | if not self._has_negative_number_optionals: | |||
|
1975 | return None | |||
|
1976 | ||||
|
1977 | # if it contains a space, it was meant to be a positional | |||
|
1978 | if ' ' in arg_string: | |||
1626 | return None |
|
1979 | return None | |
1627 |
|
1980 | |||
1628 | # it was meant to be an optional but there is no such option |
|
1981 | # 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) |
|
1982 | # in this parser (though it might be a valid option in a subparser) | |
1630 | return None, arg_string, None |
|
1983 | return None, arg_string, None | |
1631 |
|
1984 | |||
1632 |
def _get_option_ |
|
1985 | def _get_option_tuples(self, option_string): | |
1633 | result = [] |
|
1986 | result = [] | |
1634 |
|
1987 | |||
1635 | # option strings starting with two prefix characters are only |
|
1988 | # option strings starting with two prefix characters are only | |
1636 | # split at the '=' |
|
1989 | # split at the '=' | |
1637 | chars = self.prefix_chars |
|
1990 | chars = self.prefix_chars | |
1638 | if option_string[0] in chars and option_string[1] in chars: |
|
1991 | if option_string[0] in chars and option_string[1] in chars: | |
1639 | if '=' in option_string: |
|
1992 | if '=' in option_string: | |
1640 | option_prefix, explicit_arg = option_string.split('=', 1) |
|
1993 | option_prefix, explicit_arg = option_string.split('=', 1) | |
1641 | else: |
|
1994 | else: | |
1642 | option_prefix = option_string |
|
1995 | option_prefix = option_string | |
1643 | explicit_arg = None |
|
1996 | explicit_arg = None | |
1644 | tup = option_prefix, explicit_arg |
|
1997 | for option_string in self._option_string_actions: | |
1645 | result.append(tup) |
|
1998 | if option_string.startswith(option_prefix): | |
|
1999 | action = self._option_string_actions[option_string] | |||
|
2000 | tup = action, option_string, explicit_arg | |||
|
2001 | result.append(tup) | |||
|
2002 | ||||
|
2003 | # single character options can be concatenated with their arguments | |||
|
2004 | # but multiple character options always have to have their argument | |||
|
2005 | # separate | |||
|
2006 | elif option_string[0] in chars and option_string[1] not in chars: | |||
|
2007 | option_prefix = option_string | |||
|
2008 | explicit_arg = None | |||
|
2009 | short_option_prefix = option_string[:2] | |||
|
2010 | short_explicit_arg = option_string[2:] | |||
|
2011 | ||||
|
2012 | for option_string in self._option_string_actions: | |||
|
2013 | if option_string == short_option_prefix: | |||
|
2014 | action = self._option_string_actions[option_string] | |||
|
2015 | tup = action, option_string, short_explicit_arg | |||
|
2016 | result.append(tup) | |||
|
2017 | elif option_string.startswith(option_prefix): | |||
|
2018 | action = self._option_string_actions[option_string] | |||
|
2019 | tup = action, option_string, explicit_arg | |||
|
2020 | result.append(tup) | |||
1646 |
|
2021 | |||
1647 | # option strings starting with a single prefix character are |
|
2022 | # shouldn't ever get here | |
1648 | # split at all indices |
|
|||
1649 | else: |
|
2023 | else: | |
1650 | for first_index, char in enumerate(option_string): |
|
2024 | self.error(_('unexpected option string: %s') % 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 |
|
2025 | |||
1657 |
# return the collected |
|
2026 | # return the collected option tuples | |
1658 |
return result |
|
2027 | return result | |
1659 |
|
2028 | |||
1660 | def _get_nargs_pattern(self, action): |
|
2029 | def _get_nargs_pattern(self, action): | |
1661 | # in all examples below, we have to allow for '--' args |
|
2030 | # in all examples below, we have to allow for '--' args | |
1662 | # which are represented as '-' in the pattern |
|
2031 | # which are represented as '-' in the pattern | |
1663 | nargs = action.nargs |
|
2032 | nargs = action.nargs | |
1664 |
|
2033 | |||
1665 | # the default (None) is assumed to be a single argument |
|
2034 | # the default (None) is assumed to be a single argument | |
1666 | if nargs is None: |
|
2035 | if nargs is None: | |
1667 | nargs_pattern = '(-*A-*)' |
|
2036 | nargs_pattern = '(-*A-*)' | |
1668 |
|
2037 | |||
1669 | # allow zero or one arguments |
|
2038 | # allow zero or one arguments | |
1670 | elif nargs == OPTIONAL: |
|
2039 | elif nargs == OPTIONAL: | |
1671 | nargs_pattern = '(-*A?-*)' |
|
2040 | nargs_pattern = '(-*A?-*)' | |
1672 |
|
2041 | |||
1673 | # allow zero or more arguments |
|
2042 | # allow zero or more arguments | |
1674 | elif nargs == ZERO_OR_MORE: |
|
2043 | elif nargs == ZERO_OR_MORE: | |
1675 | nargs_pattern = '(-*[A-]*)' |
|
2044 | nargs_pattern = '(-*[A-]*)' | |
1676 |
|
2045 | |||
1677 | # allow one or more arguments |
|
2046 | # allow one or more arguments | |
1678 | elif nargs == ONE_OR_MORE: |
|
2047 | elif nargs == ONE_OR_MORE: | |
1679 | nargs_pattern = '(-*A[A-]*)' |
|
2048 | nargs_pattern = '(-*A[A-]*)' | |
1680 |
|
2049 | |||
1681 | # allow one argument followed by any number of options or arguments |
|
2050 | # allow one argument followed by any number of options or arguments | |
1682 | elif nargs is PARSER: |
|
2051 | elif nargs is PARSER: | |
1683 | nargs_pattern = '(-*A[-AO]*)' |
|
2052 | nargs_pattern = '(-*A[-AO]*)' | |
1684 |
|
2053 | |||
1685 |
# all others should be integers |
|
2054 | # all others should be integers | |
1686 | else: |
|
2055 | else: | |
1687 | nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) |
|
2056 | nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) | |
1688 |
|
2057 | |||
1689 |
# if this is an optional action, -- is not allowed |
|
2058 | # if this is an optional action, -- is not allowed | |
1690 | if action.option_strings: |
|
2059 | if action.option_strings: | |
1691 | nargs_pattern = nargs_pattern.replace('-*', '') |
|
2060 | nargs_pattern = nargs_pattern.replace('-*', '') | |
1692 | nargs_pattern = nargs_pattern.replace('-', '') |
|
2061 | nargs_pattern = nargs_pattern.replace('-', '') | |
1693 |
|
2062 | |||
1694 | # return the pattern |
|
2063 | # return the pattern | |
1695 | return nargs_pattern |
|
2064 | return nargs_pattern | |
1696 |
|
2065 | |||
1697 | # ======================== |
|
2066 | # ======================== | |
1698 | # Value conversion methods |
|
2067 | # Value conversion methods | |
1699 | # ======================== |
|
2068 | # ======================== | |
1700 |
|
||||
1701 | def _get_values(self, action, arg_strings): |
|
2069 | def _get_values(self, action, arg_strings): | |
1702 | # for everything but PARSER args, strip out '--' |
|
2070 | # for everything but PARSER args, strip out '--' | |
1703 | if action.nargs is not PARSER: |
|
2071 | if action.nargs is not PARSER: | |
1704 | arg_strings = [s for s in arg_strings if s != '--'] |
|
2072 | arg_strings = [s for s in arg_strings if s != '--'] | |
1705 |
|
2073 | |||
1706 | # optional argument produces a default when not present |
|
2074 | # optional argument produces a default when not present | |
1707 | if not arg_strings and action.nargs == OPTIONAL: |
|
2075 | if not arg_strings and action.nargs == OPTIONAL: | |
1708 | if action.option_strings: |
|
2076 | if action.option_strings: | |
1709 | value = action.const |
|
2077 | value = action.const | |
1710 | else: |
|
2078 | else: | |
1711 | value = action.default |
|
2079 | value = action.default | |
1712 | if isinstance(value, basestring): |
|
2080 | if isinstance(value, _basestring): | |
1713 | value = self._get_value(action, value) |
|
2081 | value = self._get_value(action, value) | |
1714 | self._check_value(action, value) |
|
2082 | self._check_value(action, value) | |
1715 |
|
2083 | |||
1716 | # when nargs='*' on a positional, if there were no command-line |
|
2084 | # when nargs='*' on a positional, if there were no command-line | |
1717 | # args, use the default if it is anything other than None |
|
2085 | # args, use the default if it is anything other than None | |
1718 | elif (not arg_strings and action.nargs == ZERO_OR_MORE and |
|
2086 | elif (not arg_strings and action.nargs == ZERO_OR_MORE and | |
1719 | not action.option_strings): |
|
2087 | not action.option_strings): | |
1720 | if action.default is not None: |
|
2088 | if action.default is not None: | |
1721 | value = action.default |
|
2089 | value = action.default | |
1722 | else: |
|
2090 | else: | |
1723 | value = arg_strings |
|
2091 | value = arg_strings | |
1724 | self._check_value(action, value) |
|
2092 | self._check_value(action, value) | |
1725 |
|
2093 | |||
1726 | # single argument or optional argument produces a single value |
|
2094 | # single argument or optional argument produces a single value | |
1727 | elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: |
|
2095 | elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: | |
1728 | arg_string, = arg_strings |
|
2096 | arg_string, = arg_strings | |
1729 | value = self._get_value(action, arg_string) |
|
2097 | value = self._get_value(action, arg_string) | |
1730 | self._check_value(action, value) |
|
2098 | self._check_value(action, value) | |
1731 |
|
2099 | |||
1732 | # PARSER arguments convert all values, but check only the first |
|
2100 | # PARSER arguments convert all values, but check only the first | |
1733 | elif action.nargs is PARSER: |
|
2101 | elif action.nargs is PARSER: | |
1734 |
value = |
|
2102 | value = [self._get_value(action, v) for v in arg_strings] | |
1735 | self._check_value(action, value[0]) |
|
2103 | self._check_value(action, value[0]) | |
1736 |
|
2104 | |||
1737 | # all other types of nargs produce a list |
|
2105 | # all other types of nargs produce a list | |
1738 | else: |
|
2106 | else: | |
1739 |
value = |
|
2107 | value = [self._get_value(action, v) for v in arg_strings] | |
1740 | for v in value: |
|
2108 | for v in value: | |
1741 | self._check_value(action, v) |
|
2109 | self._check_value(action, v) | |
1742 |
|
2110 | |||
1743 |
# return the converted value |
|
2111 | # return the converted value | |
1744 | return value |
|
2112 | return value | |
1745 |
|
2113 | |||
1746 | def _get_value(self, action, arg_string): |
|
2114 | def _get_value(self, action, arg_string): | |
1747 | type_func = self._registry_get('type', action.type, action.type) |
|
2115 | type_func = self._registry_get('type', action.type, action.type) | |
1748 |
if not |
|
2116 | if not hasattr(type_func, '__call__'): | |
1749 | msg = _('%r is not callable') |
|
2117 | msg = _('%r is not callable') | |
1750 | raise ArgumentError(action, msg % type_func) |
|
2118 | raise ArgumentError(action, msg % type_func) | |
1751 |
|
2119 | |||
1752 | # convert the value to the appropriate type |
|
2120 | # convert the value to the appropriate type | |
1753 | try: |
|
2121 | try: | |
1754 | result = type_func(arg_string) |
|
2122 | result = type_func(arg_string) | |
1755 |
|
2123 | |||
1756 | # TypeErrors or ValueErrors indicate errors |
|
2124 | # TypeErrors or ValueErrors indicate errors | |
1757 | except (TypeError, ValueError): |
|
2125 | except (TypeError, ValueError): | |
1758 | name = getattr(action.type, '__name__', repr(action.type)) |
|
2126 | name = getattr(action.type, '__name__', repr(action.type)) | |
1759 | msg = _('invalid %s value: %r') |
|
2127 | msg = _('invalid %s value: %r') | |
1760 | raise ArgumentError(action, msg % (name, arg_string)) |
|
2128 | raise ArgumentError(action, msg % (name, arg_string)) | |
1761 |
|
2129 | |||
1762 | # return the converted value |
|
2130 | # return the converted value | |
1763 | return result |
|
2131 | return result | |
1764 |
|
2132 | |||
1765 | def _check_value(self, action, value): |
|
2133 | def _check_value(self, action, value): | |
1766 | # converted value must be one of the choices (if specified) |
|
2134 | # converted value must be one of the choices (if specified) | |
1767 | if action.choices is not None and value not in action.choices: |
|
2135 | if action.choices is not None and value not in action.choices: | |
1768 | tup = value, ', '.join(map(repr, action.choices)) |
|
2136 | tup = value, ', '.join(map(repr, action.choices)) | |
1769 | msg = _('invalid choice: %r (choose from %s)') % tup |
|
2137 | msg = _('invalid choice: %r (choose from %s)') % tup | |
1770 | raise ArgumentError(action, msg) |
|
2138 | raise ArgumentError(action, msg) | |
1771 |
|
2139 | |||
1772 |
|
||||
1773 |
|
||||
1774 | # ======================= |
|
2140 | # ======================= | |
1775 | # Help-formatting methods |
|
2141 | # Help-formatting methods | |
1776 | # ======================= |
|
2142 | # ======================= | |
1777 |
|
||||
1778 | def format_usage(self): |
|
2143 | def format_usage(self): | |
1779 | formatter = self._get_formatter() |
|
2144 | formatter = self._get_formatter() | |
1780 | formatter.add_usage(self.usage, |
|
2145 | formatter.add_usage(self.usage, self._actions, | |
1781 |
self._ |
|
2146 | self._mutually_exclusive_groups) | |
1782 | self._get_positional_actions()) |
|
|||
1783 | return formatter.format_help() |
|
2147 | return formatter.format_help() | |
1784 |
|
2148 | |||
1785 | def format_help(self): |
|
2149 | def format_help(self): | |
1786 | formatter = self._get_formatter() |
|
2150 | formatter = self._get_formatter() | |
1787 |
|
2151 | |||
1788 | # usage |
|
2152 | # usage | |
1789 | formatter.add_usage(self.usage, |
|
2153 | formatter.add_usage(self.usage, self._actions, | |
1790 |
self._ |
|
2154 | self._mutually_exclusive_groups) | |
1791 | self._get_positional_actions()) |
|
|||
1792 |
|
2155 | |||
1793 |
# description |
|
2156 | # description | |
1794 | formatter.add_text(self.description) |
|
2157 | formatter.add_text(self.description) | |
1795 |
|
2158 | |||
1796 | # positionals |
|
2159 | # positionals, optionals and user-defined groups | |
1797 | formatter.start_section(_('positional arguments')) |
|
2160 | for action_group in self._action_groups: | |
1798 |
formatter. |
|
2161 | formatter.start_section(action_group.title) | |
1799 |
formatter. |
|
2162 | formatter.add_text(action_group.description) | |
1800 |
|
2163 | formatter.add_arguments(action_group._group_actions) | ||
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() |
|
2164 | formatter.end_section() | |
1813 |
|
2165 | |||
1814 | # epilog |
|
2166 | # epilog | |
1815 | formatter.add_text(self.epilog) |
|
2167 | formatter.add_text(self.epilog) | |
1816 |
|
2168 | |||
1817 | # determine help from format above |
|
2169 | # determine help from format above | |
1818 | return formatter.format_help() |
|
2170 | return formatter.format_help() | |
1819 |
|
2171 | |||
1820 | def format_version(self): |
|
2172 | def format_version(self): | |
1821 | formatter = self._get_formatter() |
|
2173 | formatter = self._get_formatter() | |
1822 | formatter.add_text(self.version) |
|
2174 | formatter.add_text(self.version) | |
1823 | return formatter.format_help() |
|
2175 | return formatter.format_help() | |
1824 |
|
2176 | |||
1825 | def _get_formatter(self): |
|
2177 | def _get_formatter(self): | |
1826 | return self.formatter_class(prog=self.prog) |
|
2178 | return self.formatter_class(prog=self.prog) | |
1827 |
|
2179 | |||
1828 | # ===================== |
|
2180 | # ===================== | |
1829 | # Help-printing methods |
|
2181 | # Help-printing methods | |
1830 | # ===================== |
|
2182 | # ===================== | |
1831 |
|
||||
1832 | def print_usage(self, file=None): |
|
2183 | def print_usage(self, file=None): | |
1833 | self._print_message(self.format_usage(), file) |
|
2184 | self._print_message(self.format_usage(), file) | |
1834 |
|
2185 | |||
1835 | def print_help(self, file=None): |
|
2186 | def print_help(self, file=None): | |
1836 | self._print_message(self.format_help(), file) |
|
2187 | self._print_message(self.format_help(), file) | |
1837 |
|
2188 | |||
1838 | def print_version(self, file=None): |
|
2189 | def print_version(self, file=None): | |
1839 | self._print_message(self.format_version(), file) |
|
2190 | self._print_message(self.format_version(), file) | |
1840 |
|
2191 | |||
1841 | def _print_message(self, message, file=None): |
|
2192 | def _print_message(self, message, file=None): | |
1842 | if message: |
|
2193 | if message: | |
1843 | if file is None: |
|
2194 | if file is None: | |
1844 | file = _sys.stderr |
|
2195 | file = _sys.stderr | |
1845 | file.write(message) |
|
2196 | file.write(message) | |
1846 |
|
2197 | |||
1847 |
|
||||
1848 | # =============== |
|
2198 | # =============== | |
1849 | # Exiting methods |
|
2199 | # Exiting methods | |
1850 | # =============== |
|
2200 | # =============== | |
1851 |
|
||||
1852 | def exit(self, status=0, message=None): |
|
2201 | def exit(self, status=0, message=None): | |
1853 | if message: |
|
2202 | if message: | |
1854 | _sys.stderr.write(message) |
|
2203 | _sys.stderr.write(message) | |
1855 | _sys.exit(status) |
|
2204 | _sys.exit(status) | |
1856 |
|
2205 | |||
1857 | def error(self, message): |
|
2206 | def error(self, message): | |
1858 | """error(message: string) |
|
2207 | """error(message: string) | |
1859 |
|
2208 | |||
1860 | Prints a usage message incorporating the message to stderr and |
|
2209 | Prints a usage message incorporating the message to stderr and | |
1861 | exits. |
|
2210 | exits. | |
1862 |
|
2211 | |||
1863 | If you override this in a subclass, it should not return -- it |
|
2212 | If you override this in a subclass, it should not return -- it | |
1864 | should either exit or raise an exception. |
|
2213 | should either exit or raise an exception. | |
1865 | """ |
|
2214 | """ | |
1866 | self.print_usage(_sys.stderr) |
|
2215 | self.print_usage(_sys.stderr) | |
1867 | self.exit(2, _('%s: error: %s\n') % (self.prog, message)) |
|
2216 | self.exit(2, _('%s: error: %s\n') % (self.prog, message)) |
@@ -1,374 +1,377 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 |
|
2 | |||
3 | """Test template for complete engine object""" |
|
3 | """Test template for complete engine object""" | |
4 |
|
4 | |||
5 | __docformat__ = "restructuredtext en" |
|
5 | __docformat__ = "restructuredtext en" | |
6 |
|
6 | |||
7 | #------------------------------------------------------------------------------- |
|
7 | #------------------------------------------------------------------------------- | |
8 | # Copyright (C) 2008 The IPython Development Team |
|
8 | # Copyright (C) 2008 The IPython Development Team | |
9 | # |
|
9 | # | |
10 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | # Distributed under the terms of the BSD License. The full license is in | |
11 | # the file COPYING, distributed as part of this software. |
|
11 | # the file COPYING, distributed as part of this software. | |
12 | #------------------------------------------------------------------------------- |
|
12 | #------------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 | #------------------------------------------------------------------------------- |
|
14 | #------------------------------------------------------------------------------- | |
15 | # Imports |
|
15 | # Imports | |
16 | #------------------------------------------------------------------------------- |
|
16 | #------------------------------------------------------------------------------- | |
17 |
|
17 | |||
18 | import cPickle as pickle |
|
18 | import cPickle as pickle | |
19 |
|
19 | |||
20 | from twisted.internet import defer, reactor |
|
20 | from twisted.internet import defer, reactor | |
21 | from twisted.python import failure |
|
21 | from twisted.python import failure | |
22 | from twisted.application import service |
|
22 | from twisted.application import service | |
23 | import zope.interface as zi |
|
23 | import zope.interface as zi | |
24 |
|
24 | |||
25 | from IPython.kernel import newserialized |
|
25 | from IPython.kernel import newserialized | |
26 | from IPython.kernel import error |
|
26 | from IPython.kernel import error | |
27 | from IPython.kernel.pickleutil import can, uncan |
|
27 | from IPython.kernel.pickleutil import can, uncan | |
28 | import IPython.kernel.engineservice as es |
|
28 | import IPython.kernel.engineservice as es | |
29 | from IPython.kernel.core.interpreter import Interpreter |
|
29 | from IPython.kernel.core.interpreter import Interpreter | |
30 | from IPython.testing.parametric import Parametric, parametric |
|
30 | from IPython.testing.parametric import Parametric, parametric | |
31 |
|
31 | |||
32 | #------------------------------------------------------------------------------- |
|
32 | #------------------------------------------------------------------------------- | |
33 | # Tests |
|
33 | # Tests | |
34 | #------------------------------------------------------------------------------- |
|
34 | #------------------------------------------------------------------------------- | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | # A sequence of valid commands run through execute |
|
37 | # A sequence of valid commands run through execute | |
38 | validCommands = ['a=5', |
|
38 | validCommands = ['a=5', | |
39 | 'b=10', |
|
39 | 'b=10', | |
40 | 'a=5; b=10; c=a+b', |
|
40 | 'a=5; b=10; c=a+b', | |
41 | 'import math; 2.0*math.pi', |
|
41 | 'import math; 2.0*math.pi', | |
42 | """def f(): |
|
42 | """def f(): | |
43 | result = 0.0 |
|
43 | result = 0.0 | |
44 | for i in range(10): |
|
44 | for i in range(10): | |
45 | result += i |
|
45 | result += i | |
46 | """, |
|
46 | """, | |
47 | 'if 1<2: a=5', |
|
47 | 'if 1<2: a=5', | |
48 | """import time |
|
48 | """import time | |
49 | time.sleep(0.1)""", |
|
49 | time.sleep(0.1)""", | |
50 | """from math import cos; |
|
50 | """from math import cos; | |
51 | x = 1.0*cos(0.5)""", # Semicolons lead to Discard ast nodes that should be discarded |
|
51 | x = 1.0*cos(0.5)""", # Semicolons lead to Discard ast nodes that should be discarded | |
52 | """s = 1 |
|
52 | """s = 1 | |
53 | s = set() |
|
53 | s = set() | |
54 | """, # Trailing whitespace should be allowed. |
|
54 | """, # Trailing whitespace should be allowed. | |
55 | """import math |
|
55 | """import math | |
56 | math.cos(1.0)""", # Test a method call with a discarded return value |
|
56 | math.cos(1.0)""", # Test a method call with a discarded return value | |
57 | """x=1.0234 |
|
57 | """x=1.0234 | |
58 | a=5; b=10""", # Test an embedded semicolon |
|
58 | a=5; b=10""", # Test an embedded semicolon | |
59 | """x=1.0234 |
|
59 | """x=1.0234 | |
60 | a=5; b=10;""" # Test both an embedded and trailing semicolon |
|
60 | a=5; b=10;""" # Test both an embedded and trailing semicolon | |
61 | ] |
|
61 | ] | |
62 |
|
62 | |||
63 | # A sequence of commands that raise various exceptions |
|
63 | # A sequence of commands that raise various exceptions | |
64 | invalidCommands = [('a=1/0',ZeroDivisionError), |
|
64 | invalidCommands = [('a=1/0',ZeroDivisionError), | |
65 | ('print v',NameError), |
|
65 | ('print v',NameError), | |
66 | ('l=[];l[0]',IndexError), |
|
66 | ('l=[];l[0]',IndexError), | |
67 | ("d={};d['a']",KeyError), |
|
67 | ("d={};d['a']",KeyError), | |
68 | ("assert 1==0",AssertionError), |
|
68 | ("assert 1==0",AssertionError), | |
69 | ("import abababsdbfsbaljasdlja",ImportError), |
|
69 | ("import abababsdbfsbaljasdlja",ImportError), | |
70 | ("raise Exception()",Exception)] |
|
70 | ("raise Exception()",Exception)] | |
71 |
|
71 | |||
72 | def testf(x): |
|
72 | def testf(x): | |
73 | return 2.0*x |
|
73 | return 2.0*x | |
74 |
|
74 | |||
75 | globala = 99 |
|
75 | globala = 99 | |
76 |
|
76 | |||
77 | def testg(x): |
|
77 | def testg(x): | |
78 | return globala*x |
|
78 | return globala*x | |
79 |
|
79 | |||
80 | class IEngineCoreTestCase(object): |
|
80 | class IEngineCoreTestCase(object): | |
81 | """Test an IEngineCore implementer.""" |
|
81 | """Test an IEngineCore implementer.""" | |
82 |
|
82 | |||
83 | def createShell(self): |
|
83 | def createShell(self): | |
84 | return Interpreter() |
|
84 | return Interpreter() | |
85 |
|
85 | |||
86 | def catchQueueCleared(self, f): |
|
86 | def catchQueueCleared(self, f): | |
87 | try: |
|
87 | try: | |
88 | f.raiseException() |
|
88 | f.raiseException() | |
89 | except error.QueueCleared: |
|
89 | except error.QueueCleared: | |
90 | pass |
|
90 | pass | |
91 |
|
91 | |||
92 | def testIEngineCoreInterface(self): |
|
92 | def testIEngineCoreInterface(self): | |
93 | """Does self.engine claim to implement IEngineCore?""" |
|
93 | """Does self.engine claim to implement IEngineCore?""" | |
94 | self.assert_(es.IEngineCore.providedBy(self.engine)) |
|
94 | self.assert_(es.IEngineCore.providedBy(self.engine)) | |
95 |
|
95 | |||
96 | def testIEngineCoreInterfaceMethods(self): |
|
96 | def testIEngineCoreInterfaceMethods(self): | |
97 | """Does self.engine have the methods and attributes in IEngineCore.""" |
|
97 | """Does self.engine have the methods and attributes in IEngineCore.""" | |
98 | for m in list(es.IEngineCore): |
|
98 | for m in list(es.IEngineCore): | |
99 | self.assert_(hasattr(self.engine, m)) |
|
99 | self.assert_(hasattr(self.engine, m)) | |
100 |
|
100 | |||
101 | def testIEngineCoreDeferreds(self): |
|
101 | def testIEngineCoreDeferreds(self): | |
102 | d = self.engine.execute('a=5') |
|
102 | d = self.engine.execute('a=5') | |
103 | d.addCallback(lambda _: self.engine.pull('a')) |
|
103 | d.addCallback(lambda _: self.engine.pull('a')) | |
104 | d.addCallback(lambda _: self.engine.get_result()) |
|
104 | d.addCallback(lambda _: self.engine.get_result()) | |
105 | d.addCallback(lambda _: self.engine.keys()) |
|
105 | d.addCallback(lambda _: self.engine.keys()) | |
106 | d.addCallback(lambda _: self.engine.push(dict(a=10))) |
|
106 | d.addCallback(lambda _: self.engine.push(dict(a=10))) | |
107 | return d |
|
107 | return d | |
108 |
|
108 | |||
109 | def runTestExecute(self, cmd): |
|
109 | def runTestExecute(self, cmd): | |
110 | self.shell = Interpreter() |
|
110 | self.shell = Interpreter() | |
111 | actual = self.shell.execute(cmd) |
|
111 | actual = self.shell.execute(cmd) | |
112 | def compare(computed): |
|
112 | def compare(computed): | |
113 | actual['id'] = computed['id'] |
|
113 | actual['id'] = computed['id'] | |
114 | self.assertEquals(actual, computed) |
|
114 | self.assertEquals(actual, computed) | |
115 | d = self.engine.execute(cmd) |
|
115 | d = self.engine.execute(cmd) | |
116 | d.addCallback(compare) |
|
116 | d.addCallback(compare) | |
117 | return d |
|
117 | return d | |
118 |
|
118 | |||
119 | @parametric |
|
119 | @parametric | |
120 | def testExecute(cls): |
|
120 | def testExecute(cls): | |
121 | return [(cls.runTestExecute, cmd) for cmd in validCommands] |
|
121 | return [(cls.runTestExecute, cmd) for cmd in validCommands] | |
122 |
|
122 | |||
123 | def runTestExecuteFailures(self, cmd, exc): |
|
123 | def runTestExecuteFailures(self, cmd, exc): | |
124 | def compare(f): |
|
124 | def compare(f): | |
125 | self.assertRaises(exc, f.raiseException) |
|
125 | self.assertRaises(exc, f.raiseException) | |
126 | d = self.engine.execute(cmd) |
|
126 | d = self.engine.execute(cmd) | |
127 | d.addErrback(compare) |
|
127 | d.addErrback(compare) | |
128 | return d |
|
128 | return d | |
129 |
|
129 | |||
130 | @parametric |
|
130 | @parametric | |
131 | def testExecuteFailuresEngineService(cls): |
|
131 | def testExecuteFailuresEngineService(cls): | |
132 | return [(cls.runTestExecuteFailures, cmd, exc) |
|
132 | return [(cls.runTestExecuteFailures, cmd, exc) | |
133 | for cmd, exc in invalidCommands] |
|
133 | for cmd, exc in invalidCommands] | |
134 |
|
134 | |||
135 | def runTestPushPull(self, o): |
|
135 | def runTestPushPull(self, o): | |
136 | d = self.engine.push(dict(a=o)) |
|
136 | d = self.engine.push(dict(a=o)) | |
137 | d.addCallback(lambda r: self.engine.pull('a')) |
|
137 | d.addCallback(lambda r: self.engine.pull('a')) | |
138 | d.addCallback(lambda r: self.assertEquals(o,r)) |
|
138 | d.addCallback(lambda r: self.assertEquals(o,r)) | |
139 | return d |
|
139 | return d | |
140 |
|
140 | |||
141 | @parametric |
|
141 | @parametric | |
142 | def testPushPull(cls): |
|
142 | def testPushPull(cls): | |
143 | objs = [10,"hi there",1.2342354,{"p":(1,2)},None] |
|
143 | objs = [10,"hi there",1.2342354,{"p":(1,2)},None] | |
144 | return [(cls.runTestPushPull, o) for o in objs] |
|
144 | return [(cls.runTestPushPull, o) for o in objs] | |
145 |
|
145 | |||
146 | def testPullNameError(self): |
|
146 | def testPullNameError(self): | |
147 | d = self.engine.push(dict(a=5)) |
|
147 | d = self.engine.push(dict(a=5)) | |
148 | d.addCallback(lambda _:self.engine.reset()) |
|
148 | d.addCallback(lambda _:self.engine.reset()) | |
149 | d.addCallback(lambda _: self.engine.pull("a")) |
|
149 | d.addCallback(lambda _: self.engine.pull("a")) | |
150 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) |
|
150 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) | |
151 | return d |
|
151 | return d | |
152 |
|
152 | |||
153 | def testPushPullFailures(self): |
|
153 | def testPushPullFailures(self): | |
154 | d = self.engine.pull('a') |
|
154 | d = self.engine.pull('a') | |
155 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) |
|
155 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) | |
156 | d.addCallback(lambda _: self.engine.execute('l = lambda x: x')) |
|
156 | d.addCallback(lambda _: self.engine.execute('l = lambda x: x')) | |
157 | d.addCallback(lambda _: self.engine.pull('l')) |
|
157 | d.addCallback(lambda _: self.engine.pull('l')) | |
158 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) |
|
158 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) | |
159 | d.addCallback(lambda _: self.engine.push(dict(l=lambda x: x))) |
|
159 | d.addCallback(lambda _: self.engine.push(dict(l=lambda x: x))) | |
160 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) |
|
160 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) | |
161 | return d |
|
161 | return d | |
162 |
|
162 | |||
163 | def testPushPullArray(self): |
|
163 | def testPushPullArray(self): | |
164 | try: |
|
164 | try: | |
165 | import numpy |
|
165 | import numpy | |
166 | except: |
|
166 | except: | |
167 | return |
|
167 | return | |
168 | a = numpy.random.random(1000) |
|
168 | a = numpy.random.random(1000) | |
169 | d = self.engine.push(dict(a=a)) |
|
169 | d = self.engine.push(dict(a=a)) | |
170 | d.addCallback(lambda _: self.engine.pull('a')) |
|
170 | d.addCallback(lambda _: self.engine.pull('a')) | |
171 | d.addCallback(lambda b: b==a) |
|
171 | d.addCallback(lambda b: b==a) | |
172 | d.addCallback(lambda c: c.all()) |
|
172 | d.addCallback(lambda c: c.all()) | |
173 | return self.assertDeferredEquals(d, True) |
|
173 | return self.assertDeferredEquals(d, True) | |
174 |
|
174 | |||
175 | def testPushFunction(self): |
|
175 | def testPushFunction(self): | |
176 |
|
176 | |||
177 | d = self.engine.push_function(dict(f=testf)) |
|
177 | d = self.engine.push_function(dict(f=testf)) | |
178 | d.addCallback(lambda _: self.engine.execute('result = f(10)')) |
|
178 | d.addCallback(lambda _: self.engine.execute('result = f(10)')) | |
179 | d.addCallback(lambda _: self.engine.pull('result')) |
|
179 | d.addCallback(lambda _: self.engine.pull('result')) | |
180 | d.addCallback(lambda r: self.assertEquals(r, testf(10))) |
|
180 | d.addCallback(lambda r: self.assertEquals(r, testf(10))) | |
181 | return d |
|
181 | return d | |
182 |
|
182 | |||
183 | def testPullFunction(self): |
|
183 | def testPullFunction(self): | |
184 | d = self.engine.push_function(dict(f=testf, g=testg)) |
|
184 | d = self.engine.push_function(dict(f=testf, g=testg)) | |
185 | d.addCallback(lambda _: self.engine.pull_function(('f','g'))) |
|
185 | d.addCallback(lambda _: self.engine.pull_function(('f','g'))) | |
186 | d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10))) |
|
186 | d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10))) | |
187 | return d |
|
187 | return d | |
188 |
|
188 | |||
189 | def testPushFunctionGlobal(self): |
|
189 | def testPushFunctionGlobal(self): | |
190 | """Make sure that pushed functions pick up the user's namespace for globals.""" |
|
190 | """Make sure that pushed functions pick up the user's namespace for globals.""" | |
191 | d = self.engine.push(dict(globala=globala)) |
|
191 | d = self.engine.push(dict(globala=globala)) | |
192 | d.addCallback(lambda _: self.engine.push_function(dict(g=testg))) |
|
192 | d.addCallback(lambda _: self.engine.push_function(dict(g=testg))) | |
193 | d.addCallback(lambda _: self.engine.execute('result = g(10)')) |
|
193 | d.addCallback(lambda _: self.engine.execute('result = g(10)')) | |
194 | d.addCallback(lambda _: self.engine.pull('result')) |
|
194 | d.addCallback(lambda _: self.engine.pull('result')) | |
195 | d.addCallback(lambda r: self.assertEquals(r, testg(10))) |
|
195 | d.addCallback(lambda r: self.assertEquals(r, testg(10))) | |
196 | return d |
|
196 | return d | |
197 |
|
197 | |||
198 | def testGetResultFailure(self): |
|
198 | def testGetResultFailure(self): | |
199 | d = self.engine.get_result(None) |
|
199 | d = self.engine.get_result(None) | |
200 | d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException)) |
|
200 | d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException)) | |
201 | d.addCallback(lambda _: self.engine.get_result(10)) |
|
201 | d.addCallback(lambda _: self.engine.get_result(10)) | |
202 | d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException)) |
|
202 | d.addErrback(lambda f: self.assertRaises(IndexError, f.raiseException)) | |
203 | return d |
|
203 | return d | |
204 |
|
204 | |||
205 | def runTestGetResult(self, cmd): |
|
205 | def runTestGetResult(self, cmd): | |
206 | self.shell = Interpreter() |
|
206 | self.shell = Interpreter() | |
207 | actual = self.shell.execute(cmd) |
|
207 | actual = self.shell.execute(cmd) | |
208 | def compare(computed): |
|
208 | def compare(computed): | |
209 | actual['id'] = computed['id'] |
|
209 | actual['id'] = computed['id'] | |
210 | self.assertEquals(actual, computed) |
|
210 | self.assertEquals(actual, computed) | |
211 | d = self.engine.execute(cmd) |
|
211 | d = self.engine.execute(cmd) | |
212 | d.addCallback(lambda r: self.engine.get_result(r['number'])) |
|
212 | d.addCallback(lambda r: self.engine.get_result(r['number'])) | |
213 | d.addCallback(compare) |
|
213 | d.addCallback(compare) | |
214 | return d |
|
214 | return d | |
215 |
|
215 | |||
216 | @parametric |
|
216 | @parametric | |
217 | def testGetResult(cls): |
|
217 | def testGetResult(cls): | |
218 | return [(cls.runTestGetResult, cmd) for cmd in validCommands] |
|
218 | return [(cls.runTestGetResult, cmd) for cmd in validCommands] | |
219 |
|
219 | |||
220 | def testGetResultDefault(self): |
|
220 | def testGetResultDefault(self): | |
221 | cmd = 'a=5' |
|
221 | cmd = 'a=5' | |
222 | shell = self.createShell() |
|
222 | shell = self.createShell() | |
223 | shellResult = shell.execute(cmd) |
|
223 | shellResult = shell.execute(cmd) | |
224 | def popit(dikt, key): |
|
224 | def popit(dikt, key): | |
225 | dikt.pop(key) |
|
225 | dikt.pop(key) | |
226 | return dikt |
|
226 | return dikt | |
227 | d = self.engine.execute(cmd) |
|
227 | d = self.engine.execute(cmd) | |
228 | d.addCallback(lambda _: self.engine.get_result()) |
|
228 | d.addCallback(lambda _: self.engine.get_result()) | |
229 | d.addCallback(lambda r: self.assertEquals(shellResult, popit(r,'id'))) |
|
229 | d.addCallback(lambda r: self.assertEquals(shellResult, popit(r,'id'))) | |
230 | return d |
|
230 | return d | |
231 |
|
231 | |||
232 | def testKeys(self): |
|
232 | def testKeys(self): | |
233 | d = self.engine.keys() |
|
233 | d = self.engine.keys() | |
234 | d.addCallback(lambda s: isinstance(s, list)) |
|
234 | d.addCallback(lambda s: isinstance(s, list)) | |
235 | d.addCallback(lambda r: self.assertEquals(r, True)) |
|
235 | d.addCallback(lambda r: self.assertEquals(r, True)) | |
236 | return d |
|
236 | return d | |
237 |
|
237 | |||
238 | Parametric(IEngineCoreTestCase) |
|
238 | Parametric(IEngineCoreTestCase) | |
239 |
|
239 | |||
240 | class IEngineSerializedTestCase(object): |
|
240 | class IEngineSerializedTestCase(object): | |
241 | """Test an IEngineCore implementer.""" |
|
241 | """Test an IEngineCore implementer.""" | |
242 |
|
242 | |||
243 | def testIEngineSerializedInterface(self): |
|
243 | def testIEngineSerializedInterface(self): | |
244 | """Does self.engine claim to implement IEngineCore?""" |
|
244 | """Does self.engine claim to implement IEngineCore?""" | |
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 IEngineCore.""" |
|
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 | |||
252 | def testIEngineSerializedDeferreds(self): |
|
252 | def testIEngineSerializedDeferreds(self): | |
253 | dList = [] |
|
253 | dList = [] | |
254 | d = self.engine.push_serialized(dict(key=newserialized.serialize(12345))) |
|
254 | d = self.engine.push_serialized(dict(key=newserialized.serialize(12345))) | |
255 | self.assert_(isinstance(d, defer.Deferred)) |
|
255 | self.assert_(isinstance(d, defer.Deferred)) | |
256 | dList.append(d) |
|
256 | dList.append(d) | |
257 | d = self.engine.pull_serialized('key') |
|
257 | d = self.engine.pull_serialized('key') | |
258 | self.assert_(isinstance(d, defer.Deferred)) |
|
258 | self.assert_(isinstance(d, defer.Deferred)) | |
259 | dList.append(d) |
|
259 | dList.append(d) | |
260 | D = defer.DeferredList(dList) |
|
260 | D = defer.DeferredList(dList) | |
261 | return D |
|
261 | return D | |
262 |
|
262 | |||
263 | def testPushPullSerialized(self): |
|
263 | def testPushPullSerialized(self): | |
264 | objs = [10,"hi there",1.2342354,{"p":(1,2)}] |
|
264 | objs = [10,"hi there",1.2342354,{"p":(1,2)}] | |
265 | d = defer.succeed(None) |
|
265 | d = defer.succeed(None) | |
266 | for o in objs: |
|
266 | for o in objs: | |
267 | self.engine.push_serialized(dict(key=newserialized.serialize(o))) |
|
267 | self.engine.push_serialized(dict(key=newserialized.serialize(o))) | |
268 | value = self.engine.pull_serialized('key') |
|
268 | value = self.engine.pull_serialized('key') | |
269 | value.addCallback(lambda serial: newserialized.IUnSerialized(serial).getObject()) |
|
269 | value.addCallback(lambda serial: newserialized.IUnSerialized(serial).getObject()) | |
270 | d = self.assertDeferredEquals(value,o,d) |
|
270 | d = self.assertDeferredEquals(value,o,d) | |
271 | return d |
|
271 | return d | |
272 |
|
272 | |||
273 | def testPullSerializedFailures(self): |
|
273 | def testPullSerializedFailures(self): | |
274 | d = self.engine.pull_serialized('a') |
|
274 | d = self.engine.pull_serialized('a') | |
275 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) |
|
275 | d.addErrback(lambda f: self.assertRaises(NameError, f.raiseException)) | |
276 | d.addCallback(lambda _: self.engine.execute('l = lambda x: x')) |
|
276 | d.addCallback(lambda _: self.engine.execute('l = lambda x: x')) | |
277 | d.addCallback(lambda _: self.engine.pull_serialized('l')) |
|
277 | d.addCallback(lambda _: self.engine.pull_serialized('l')) | |
278 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) |
|
278 | d.addErrback(lambda f: self.assertRaises(pickle.PicklingError, f.raiseException)) | |
279 | return d |
|
279 | return d | |
280 |
|
280 | |||
281 | Parametric(IEngineSerializedTestCase) |
|
281 | Parametric(IEngineSerializedTestCase) | |
282 |
|
282 | |||
283 | class IEngineQueuedTestCase(object): |
|
283 | class IEngineQueuedTestCase(object): | |
284 | """Test an IEngineQueued implementer.""" |
|
284 | """Test an IEngineQueued implementer.""" | |
285 |
|
285 | |||
286 | def testIEngineQueuedInterface(self): |
|
286 | def testIEngineQueuedInterface(self): | |
287 | """Does self.engine claim to implement IEngineQueued?""" |
|
287 | """Does self.engine claim to implement IEngineQueued?""" | |
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 IEngineQueued.""" |
|
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 | |||
295 | def testIEngineQueuedDeferreds(self): |
|
295 | def testIEngineQueuedDeferreds(self): | |
296 | dList = [] |
|
296 | dList = [] | |
297 | d = self.engine.clear_queue() |
|
297 | d = self.engine.clear_queue() | |
298 | self.assert_(isinstance(d, defer.Deferred)) |
|
298 | self.assert_(isinstance(d, defer.Deferred)) | |
299 | dList.append(d) |
|
299 | dList.append(d) | |
300 | d = self.engine.queue_status() |
|
300 | d = self.engine.queue_status() | |
301 | self.assert_(isinstance(d, defer.Deferred)) |
|
301 | self.assert_(isinstance(d, defer.Deferred)) | |
302 | dList.append(d) |
|
302 | dList.append(d) | |
303 | D = defer.DeferredList(dList) |
|
303 | D = defer.DeferredList(dList) | |
304 | return D |
|
304 | return D | |
305 |
|
305 | |||
306 | def testClearQueue(self): |
|
306 | def testClearQueue(self): | |
307 | result = self.engine.clear_queue() |
|
307 | result = self.engine.clear_queue() | |
308 | d1 = self.assertDeferredEquals(result, None) |
|
308 | d1 = self.assertDeferredEquals(result, None) | |
309 | d1.addCallback(lambda _: self.engine.queue_status()) |
|
309 | d1.addCallback(lambda _: self.engine.queue_status()) | |
310 | d2 = self.assertDeferredEquals(d1, {'queue':[], 'pending':'None'}) |
|
310 | d2 = self.assertDeferredEquals(d1, {'queue':[], 'pending':'None'}) | |
311 | return d2 |
|
311 | return d2 | |
312 |
|
312 | |||
313 | def testQueueStatus(self): |
|
313 | def testQueueStatus(self): | |
314 | result = self.engine.queue_status() |
|
314 | result = self.engine.queue_status() | |
315 | result.addCallback(lambda r: 'queue' in r and 'pending' in r) |
|
315 | result.addCallback(lambda r: 'queue' in r and 'pending' in r) | |
316 | d = self.assertDeferredEquals(result, True) |
|
316 | d = self.assertDeferredEquals(result, True) | |
317 | return d |
|
317 | return d | |
318 |
|
318 | |||
319 | Parametric(IEngineQueuedTestCase) |
|
319 | Parametric(IEngineQueuedTestCase) | |
320 |
|
320 | |||
321 | class IEnginePropertiesTestCase(object): |
|
321 | class IEnginePropertiesTestCase(object): | |
322 | """Test an IEngineProperties implementor.""" |
|
322 | """Test an IEngineProperties implementor.""" | |
323 |
|
323 | |||
324 | def testIEnginePropertiesInterface(self): |
|
324 | def testIEnginePropertiesInterface(self): | |
325 | """Does self.engine claim to implement IEngineProperties?""" |
|
325 | """Does self.engine claim to implement IEngineProperties?""" | |
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 IEngineProperties.""" |
|
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 | |||
333 | def testGetSetProperties(self): |
|
333 | def testGetSetProperties(self): | |
334 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) |
|
334 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) | |
335 | d = self.engine.set_properties(dikt) |
|
335 | d = self.engine.set_properties(dikt) | |
336 | d.addCallback(lambda r: self.engine.get_properties()) |
|
336 | d.addCallback(lambda r: self.engine.get_properties()) | |
337 | d = self.assertDeferredEquals(d, dikt) |
|
337 | d = self.assertDeferredEquals(d, dikt) | |
338 | d.addCallback(lambda r: self.engine.get_properties(('c',))) |
|
338 | d.addCallback(lambda r: self.engine.get_properties(('c',))) | |
339 | d = self.assertDeferredEquals(d, {'c': dikt['c']}) |
|
339 | d = self.assertDeferredEquals(d, {'c': dikt['c']}) | |
340 | d.addCallback(lambda r: self.engine.set_properties(dict(c=False))) |
|
340 | d.addCallback(lambda r: self.engine.set_properties(dict(c=False))) | |
341 | d.addCallback(lambda r: self.engine.get_properties(('c', 'd'))) |
|
341 | d.addCallback(lambda r: self.engine.get_properties(('c', 'd'))) | |
342 | d = self.assertDeferredEquals(d, dict(c=False, d=None)) |
|
342 | d = self.assertDeferredEquals(d, dict(c=False, d=None)) | |
343 | return d |
|
343 | return d | |
344 |
|
344 | |||
345 | def testClearProperties(self): |
|
345 | def testClearProperties(self): | |
346 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) |
|
346 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) | |
347 | d = self.engine.set_properties(dikt) |
|
347 | d = self.engine.set_properties(dikt) | |
348 | d.addCallback(lambda r: self.engine.clear_properties()) |
|
348 | d.addCallback(lambda r: self.engine.clear_properties()) | |
349 | d.addCallback(lambda r: self.engine.get_properties()) |
|
349 | d.addCallback(lambda r: self.engine.get_properties()) | |
350 | d = self.assertDeferredEquals(d, {}) |
|
350 | d = self.assertDeferredEquals(d, {}) | |
351 | return d |
|
351 | return d | |
352 |
|
352 | |||
353 | def testDelHasProperties(self): |
|
353 | def testDelHasProperties(self): | |
354 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) |
|
354 | dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5)) | |
355 | d = self.engine.set_properties(dikt) |
|
355 | d = self.engine.set_properties(dikt) | |
356 | d.addCallback(lambda r: self.engine.del_properties(('b','e'))) |
|
356 | d.addCallback(lambda r: self.engine.del_properties(('b','e'))) | |
357 | d.addCallback(lambda r: self.engine.has_properties(('a','b','c','d','e'))) |
|
357 | d.addCallback(lambda r: self.engine.has_properties(('a','b','c','d','e'))) | |
358 | d = self.assertDeferredEquals(d, [True, False, True, True, False]) |
|
358 | d = self.assertDeferredEquals(d, [True, False, True, True, False]) | |
359 | return d |
|
359 | return d | |
360 |
|
360 | |||
361 | def testStrictDict(self): |
|
361 | def testStrictDict(self): | |
362 | s = """from IPython.kernel.engineservice import get_engine |
|
362 | s = """from IPython.kernel.engineservice import get_engine; p = get_engine(%s).properties"""%self.engine.id | |
363 | p = get_engine(%s).properties"""%self.engine.id |
|
|||
364 | d = self.engine.execute(s) |
|
363 | d = self.engine.execute(s) | |
|
364 | # These 3 lines cause a weird testing error on some platforms (OS X). | |||
|
365 | # I am leaving them here in case they are masking some really | |||
|
366 | # weird reactor issue. For now I will just keep my eye on this. | |||
365 | d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None")) |
|
367 | d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None")) | |
366 | d.addErrback(lambda f: self.assertRaises(error.InvalidProperty, |
|
368 | d.addErrback(lambda f: self.assertRaises(error.InvalidProperty, | |
367 | f.raiseException)) |
|
369 | f.raiseException)) | |
|
370 | # Below here seems to be fine | |||
368 | d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)")) |
|
371 | d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)")) | |
369 | d.addCallback(lambda r: self.engine.execute("p['a'].append(5)")) |
|
372 | d.addCallback(lambda r: self.engine.execute("p['a'].append(5)")) | |
370 | d.addCallback(lambda r: self.engine.get_properties('a')) |
|
373 | d.addCallback(lambda r: self.engine.get_properties('a')) | |
371 | d = self.assertDeferredEquals(d, dict(a=range(5))) |
|
374 | d = self.assertDeferredEquals(d, dict(a=range(5))) | |
372 | return d |
|
375 | return d | |
373 |
|
376 | |||
374 | Parametric(IEnginePropertiesTestCase) |
|
377 | Parametric(IEnginePropertiesTestCase) |
@@ -1,577 +1,544 b'' | |||||
1 | .. Developers should add in this file, during each release cycle, information |
|
1 | .. Developers should add in this file, during each release cycle, information | |
2 | .. about important changes they've made, in a summary format that's meant for |
|
2 | .. about important changes they've made, in a summary format that's meant for | |
3 | .. end users. For each release we normally have three sections: features, bug |
|
3 | .. end users. For each release we normally have three sections: features, bug | |
4 | .. fixes and api breakage. |
|
4 | .. fixes and api breakage. | |
5 | .. Please remember to credit the authors of the contributions by name, |
|
5 | .. Please remember to credit the authors of the contributions by name, | |
6 | .. especially when they are new users or developers who do not regularly |
|
6 | .. especially when they are new users or developers who do not regularly | |
7 | .. participate in IPython's development. |
|
7 | .. participate in IPython's development. | |
8 |
|
8 | |||
9 | .. _changes: |
|
9 | .. _changes: | |
10 |
|
10 | |||
11 | ========== |
|
11 | ========== | |
12 | What's new |
|
12 | What's new | |
13 | ========== |
|
13 | ========== | |
14 |
|
14 | |||
15 | .. contents:: |
|
|||
16 | .. |
|
|||
17 | 1 Release dev |
|
|||
18 | 1.1 New features |
|
|||
19 | 1.2 Bug fixes |
|
|||
20 | 1.3 Backwards incompatible changes |
|
|||
21 | 2 Release 0.10 |
|
|||
22 | 2.1 New features |
|
|||
23 | 2.2 Bug fixes |
|
|||
24 | 2.3 Backwards incompatible changes |
|
|||
25 | 3 Release 0.9.1 |
|
|||
26 | 4 Release 0.9 |
|
|||
27 | 4.1 New features |
|
|||
28 | 4.2 Bug fixes |
|
|||
29 | 4.3 Backwards incompatible changes |
|
|||
30 | 4.4 Changes merged in from IPython1 |
|
|||
31 | 4.4.1 New features |
|
|||
32 | 4.4.2 Bug fixes |
|
|||
33 | 4.4.3 Backwards incompatible changes |
|
|||
34 | 5 Release 0.8.4 |
|
|||
35 | 6 Release 0.8.3 |
|
|||
36 | 7 Release 0.8.2 |
|
|||
37 | 8 Older releases |
|
|||
38 | .. |
|
|||
39 |
|
||||
40 | Release dev |
|
|||
41 | =========== |
|
|||
42 |
|
||||
43 | New features |
|
|||
44 | ------------ |
|
|||
45 |
|
||||
46 | Bug fixes |
|
|||
47 | --------- |
|
|||
48 |
|
||||
49 | Backwards incompatible changes |
|
|||
50 | ------------------------------ |
|
|||
51 |
|
||||
52 |
|
||||
53 | Release 0.10 |
|
15 | Release 0.10 | |
54 | ============ |
|
16 | ============ | |
55 |
|
17 | |||
56 | This release brings months of slow but steady development, and will be the last |
|
18 | This release brings months of slow but steady development, and will be the last | |
57 | before a major restructuring and cleanup of IPython's internals that is already |
|
19 | before a major restructuring and cleanup of IPython's internals that is already | |
58 | under way. For this reason, we hope that 0.10 will be a stable and robust |
|
20 | under way. For this reason, we hope that 0.10 will be a stable and robust | |
59 | release so that while users adapt to some of the API changes that will come |
|
21 | release so that while users adapt to some of the API changes that will come | |
60 | with the refactoring that will become IPython 0.11, they can safely use 0.10 in |
|
22 | with the refactoring that will become IPython 0.11, they can safely use 0.10 in | |
61 | all existing projects with minimal changes (if any). |
|
23 | all existing projects with minimal changes (if any). | |
62 |
|
24 | |||
63 | IPython 0.10 is now a medium-sized project, with roughly (as reported by David |
|
25 | IPython 0.10 is now a medium-sized project, with roughly (as reported by David | |
64 | Wheeler's :command:`sloccount` utility) 40750 lines of Python code, and a diff |
|
26 | Wheeler's :command:`sloccount` utility) 40750 lines of Python code, and a diff | |
65 | between 0.9.1 and this release that contains almost 28000 lines of code and |
|
27 | between 0.9.1 and this release that contains almost 28000 lines of code and | |
66 | documentation. Our documentation, in PDF format, is a 495-page long PDF |
|
28 | documentation. Our documentation, in PDF format, is a 495-page long PDF | |
67 | document (also available in HTML format, both generated from the same sources). |
|
29 | document (also available in HTML format, both generated from the same sources). | |
68 |
|
30 | |||
69 | Many users and developers contributed code, features, bug reports and ideas to |
|
31 | Many users and developers contributed code, features, bug reports and ideas to | |
70 | this release. Please do not hesitate in contacting us if we've failed to |
|
32 | this release. Please do not hesitate in contacting us if we've failed to | |
71 | acknowledge your contribution here. In particular, for this release we have |
|
33 | acknowledge your contribution here. In particular, for this release we have | |
72 | contribution from the following people, a mix of new and regular names (in |
|
34 | contribution from the following people, a mix of new and regular names (in | |
73 | alphabetical order by first name): |
|
35 | alphabetical order by first name): | |
74 |
|
36 | |||
75 | * Alexander Clausen: fix #341726. |
|
37 | * Alexander Clausen: fix #341726. | |
76 | * Brian Granger: lots of work everywhere (features, bug fixes, etc). |
|
38 | * Brian Granger: lots of work everywhere (features, bug fixes, etc). | |
77 | * Daniel Ashbrook: bug report on MemoryError during compilation, now fixed. |
|
39 | * Daniel Ashbrook: bug report on MemoryError during compilation, now fixed. | |
78 | * Darren Dale: improvements to documentation build system, feedback, design |
|
40 | * Darren Dale: improvements to documentation build system, feedback, design | |
79 | ideas. |
|
41 | ideas. | |
80 | * Fernando Perez: various places. |
|
42 | * Fernando Perez: various places. | |
81 | * GaΓ«l Varoquaux: core code, ipythonx GUI, design discussions, etc. Lots... |
|
43 | * GaΓ«l Varoquaux: core code, ipythonx GUI, design discussions, etc. Lots... | |
82 | * John Hunter: suggestions, bug fixes, feedback. |
|
44 | * John Hunter: suggestions, bug fixes, feedback. | |
83 | * Jorgen Stenarson: work on many fronts, tests, fixes, win32 support, etc. |
|
45 | * Jorgen Stenarson: work on many fronts, tests, fixes, win32 support, etc. | |
84 | * Laurent DufrΓ©chou: many improvements to ipython-wx standalone app. |
|
46 | * Laurent DufrΓ©chou: many improvements to ipython-wx standalone app. | |
85 | * Lukasz Pankowski: prefilter, `%edit`, demo improvements. |
|
47 | * Lukasz Pankowski: prefilter, `%edit`, demo improvements. | |
86 | * Matt Foster: TextMate support in `%edit`. |
|
48 | * Matt Foster: TextMate support in `%edit`. | |
87 | * Nathaniel Smith: fix #237073. |
|
49 | * Nathaniel Smith: fix #237073. | |
88 | * Pauli Virtanen: fixes and improvements to extensions, documentation. |
|
50 | * Pauli Virtanen: fixes and improvements to extensions, documentation. | |
89 | * Prabhu Ramachandran: improvements to `%timeit`. |
|
51 | * Prabhu Ramachandran: improvements to `%timeit`. | |
90 | * Robert Kern: several extensions. |
|
52 | * Robert Kern: several extensions. | |
91 | * Sameer D'Costa: help on critical bug #269966. |
|
53 | * Sameer D'Costa: help on critical bug #269966. | |
92 | * Stephan Peijnik: feedback on Debian compliance and many man pages. |
|
54 | * Stephan Peijnik: feedback on Debian compliance and many man pages. | |
|
55 | * Steven Bethard: we are now shipping his :mod:`argparse` module. | |||
93 | * Tom Fetherston: many improvements to :mod:`IPython.demo` module. |
|
56 | * Tom Fetherston: many improvements to :mod:`IPython.demo` module. | |
94 | * Ville Vainio: lots of work everywhere (features, bug fixes, etc). |
|
57 | * Ville Vainio: lots of work everywhere (features, bug fixes, etc). | |
95 | * Vishal Vasta: ssh support in ipcluster. |
|
58 | * Vishal Vasta: ssh support in ipcluster. | |
96 | * Walter Doerwald: work on the :mod:`IPython.ipipe` system. |
|
59 | * Walter Doerwald: work on the :mod:`IPython.ipipe` system. | |
97 |
|
60 | |||
98 | Below we give an overview of new features, bug fixes and backwards-incompatible |
|
61 | Below we give an overview of new features, bug fixes and backwards-incompatible | |
99 | changes. For a detailed account of every change made, feel free to view the |
|
62 | changes. For a detailed account of every change made, feel free to view the | |
100 | project log with :command:`bzr log`. |
|
63 | project log with :command:`bzr log`. | |
101 |
|
64 | |||
102 | New features |
|
65 | New features | |
103 | ------------ |
|
66 | ------------ | |
104 |
|
67 | |||
105 | * New `%paste` magic automatically extracts current contents of clipboard and |
|
68 | * New `%paste` magic automatically extracts current contents of clipboard and | |
106 | pastes it directly, while correctly handling code that is indented or |
|
69 | pastes it directly, while correctly handling code that is indented or | |
107 | prepended with `>>>` or `...` python prompt markers. A very useful new |
|
70 | prepended with `>>>` or `...` python prompt markers. A very useful new | |
108 | feature contributed by Robert Kern. |
|
71 | feature contributed by Robert Kern. | |
109 |
|
72 | |||
110 | * IPython 'demos', created with the :mod:`IPython.demo` module, can now be |
|
73 | * IPython 'demos', created with the :mod:`IPython.demo` module, can now be | |
111 | created from files on disk or strings in memory. Other fixes and |
|
74 | created from files on disk or strings in memory. Other fixes and | |
112 | improvements to the demo system, by Tom Fetherston. |
|
75 | improvements to the demo system, by Tom Fetherston. | |
113 |
|
76 | |||
114 | * Added :func:`find_cmd` function to :mod:`IPython.platutils` module, to find |
|
77 | * Added :func:`find_cmd` function to :mod:`IPython.platutils` module, to find | |
115 | commands in a cross-platform manner. |
|
78 | commands in a cross-platform manner. | |
116 |
|
79 | |||
117 | * Many improvements and fixes to GaΓ«l Varoquaux's :command:`ipythonx`, a |
|
80 | * Many improvements and fixes to GaΓ«l Varoquaux's :command:`ipythonx`, a | |
118 | WX-based lightweight IPython instance that can be easily embedded in other WX |
|
81 | WX-based lightweight IPython instance that can be easily embedded in other WX | |
119 | applications. These improvements have made it possible to now have an |
|
82 | applications. These improvements have made it possible to now have an | |
120 | embedded IPython in Mayavi and other tools. |
|
83 | embedded IPython in Mayavi and other tools. | |
121 |
|
84 | |||
122 | * :class:`MultiengineClient` objects now have a :meth:`benchmark` method. |
|
85 | * :class:`MultiengineClient` objects now have a :meth:`benchmark` method. | |
123 |
|
86 | |||
124 | * The manual now includes a full set of auto-generated API documents from the |
|
87 | * The manual now includes a full set of auto-generated API documents from the | |
125 | code sources, using Sphinx and some of our own support code. We are now |
|
88 | code sources, using Sphinx and some of our own support code. We are now | |
126 | using the `Numpy Documentation Standard`_ for all docstrings, and we have |
|
89 | using the `Numpy Documentation Standard`_ for all docstrings, and we have | |
127 | tried to update as many existing ones as possible to this format. |
|
90 | tried to update as many existing ones as possible to this format. | |
128 |
|
91 | |||
129 | .. _Numpy Documentation Standard: http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard |
|
|||
130 |
|
||||
131 | * The new :mod:`IPython.Extensions.ipy_pretty` extension by Robert Kern |
|
92 | * The new :mod:`IPython.Extensions.ipy_pretty` extension by Robert Kern | |
132 | provides configurable pretty-printing. |
|
93 | provides configurable pretty-printing. | |
133 |
|
94 | |||
134 | * Many improvements to the :command:`ipython-wx` standalone WX-based IPython |
|
95 | * Many improvements to the :command:`ipython-wx` standalone WX-based IPython | |
135 | application by Laurent DufrΓ©chou. It can optionally run in a thread, and |
|
96 | application by Laurent DufrΓ©chou. It can optionally run in a thread, and | |
136 | this can be toggled at runtime (allowing the loading of Matplotlib in a |
|
97 | this can be toggled at runtime (allowing the loading of Matplotlib in a | |
137 | running session without ill effects). |
|
98 | running session without ill effects). | |
138 |
|
99 | |||
139 | * IPython includes a copy of Steven Bethard's argparse_ in the |
|
100 | * IPython includes a copy of Steven Bethard's argparse_ in the | |
140 | :mod:`IPython.external` package, so we can use it internally and it is also |
|
101 | :mod:`IPython.external` package, so we can use it internally and it is also | |
141 | available to any IPython user. By installing it in this manner, we ensure |
|
102 | available to any IPython user. By installing it in this manner, we ensure | |
142 | zero conflicts with any system-wide installation you may already have while |
|
103 | zero conflicts with any system-wide installation you may already have while | |
143 | minimizing external dependencies for new users. |
|
104 | minimizing external dependencies for new users. In IPython 0.10, We ship | |
|
105 | argparse version 1.0. | |||
144 |
|
106 | |||
145 | * An improved and much more robust test suite, that runs groups of tests in |
|
107 | * An improved and much more robust test suite, that runs groups of tests in | |
146 | separate subprocesses using either Nose or Twisted's :command:`trial` runner |
|
108 | separate subprocesses using either Nose or Twisted's :command:`trial` runner | |
147 | to ensure proper management of Twisted-using code. The test suite degrades |
|
109 | to ensure proper management of Twisted-using code. The test suite degrades | |
148 | gracefully if optional dependencies are not available, so that the |
|
110 | gracefully if optional dependencies are not available, so that the | |
149 | :command:`iptest` command can be run with only Nose installed and nothing |
|
111 | :command:`iptest` command can be run with only Nose installed and nothing | |
150 | else. We also have more and cleaner test decorators to better select tests |
|
112 | else. We also have more and cleaner test decorators to better select tests | |
151 | depending on runtime conditions, do setup/teardown, etc. |
|
113 | depending on runtime conditions, do setup/teardown, etc. | |
152 |
|
114 | |||
153 | * The new ipcluster now has a fully working ssh mode that should work on |
|
115 | * The new ipcluster now has a fully working ssh mode that should work on | |
154 | Linux, Unix and OS X. Thanks to Vishal Vatsa for implementing this! |
|
116 | Linux, Unix and OS X. Thanks to Vishal Vatsa for implementing this! | |
155 |
|
117 | |||
156 | * The wonderful TextMate editor can now be used with %edit on OS X. Thanks |
|
118 | * The wonderful TextMate editor can now be used with %edit on OS X. Thanks | |
157 | to Matt Foster for this patch. |
|
119 | to Matt Foster for this patch. | |
158 |
|
120 | |||
159 | * The documentation regarding parallel uses of IPython, including MPI and PBS, |
|
121 | * The documentation regarding parallel uses of IPython, including MPI and PBS, | |
160 | has been significantly updated and improved. |
|
122 | has been significantly updated and improved. | |
161 |
|
123 | |||
162 | * The developer guidelines in the documentation have been updated to explain |
|
124 | * The developer guidelines in the documentation have been updated to explain | |
163 | our workflow using :command:`bzr` and Launchpad. |
|
125 | our workflow using :command:`bzr` and Launchpad. | |
164 |
|
126 | |||
165 | * Fully refactored :command:`ipcluster` command line program for starting |
|
127 | * Fully refactored :command:`ipcluster` command line program for starting | |
166 | IPython clusters. This new version is a complete rewrite and 1) is fully |
|
128 | IPython clusters. This new version is a complete rewrite and 1) is fully | |
167 | cross platform (we now use Twisted's process management), 2) has much |
|
129 | cross platform (we now use Twisted's process management), 2) has much | |
168 | improved performance, 3) uses subcommands for different types of clusters, 4) |
|
130 | improved performance, 3) uses subcommands for different types of clusters, 4) | |
169 | uses argparse for parsing command line options, 5) has better support for |
|
131 | uses argparse for parsing command line options, 5) has better support for | |
170 | starting clusters using :command:`mpirun`, 6) has experimental support for |
|
132 | starting clusters using :command:`mpirun`, 6) has experimental support for | |
171 | starting engines using PBS. It can also reuse FURL files, by appropriately |
|
133 | starting engines using PBS. It can also reuse FURL files, by appropriately | |
172 | passing options to its subcommands. However, this new version of ipcluster |
|
134 | passing options to its subcommands. However, this new version of ipcluster | |
173 | should be considered a technology preview. We plan on changing the API in |
|
135 | should be considered a technology preview. We plan on changing the API in | |
174 | significant ways before it is final. |
|
136 | significant ways before it is final. | |
175 |
|
137 | |||
176 | * The :mod:`argparse` module has been added to :mod:`IPython.external`. |
|
|||
177 |
|
||||
178 | * Full description of the security model added to the docs. |
|
138 | * Full description of the security model added to the docs. | |
179 |
|
139 | |||
180 | * cd completer: show bookmarks if no other completions are available. |
|
140 | * cd completer: show bookmarks if no other completions are available. | |
181 |
|
141 | |||
182 | * sh profile: easy way to give 'title' to prompt: assign to variable |
|
142 | * sh profile: easy way to give 'title' to prompt: assign to variable | |
183 | '_prompt_title'. It looks like this:: |
|
143 | '_prompt_title'. It looks like this:: | |
184 |
|
144 | |||
185 | [~]|1> _prompt_title = 'sudo!' |
|
145 | [~]|1> _prompt_title = 'sudo!' | |
186 | sudo![~]|2> |
|
146 | sudo![~]|2> | |
187 |
|
147 | |||
188 | * %edit: If you do '%edit pasted_block', pasted_block variable gets updated |
|
148 | * %edit: If you do '%edit pasted_block', pasted_block variable gets updated | |
189 | with new data (so repeated editing makes sense) |
|
149 | with new data (so repeated editing makes sense) | |
190 |
|
150 | |||
|
151 | .. _Numpy Documentation Standard: http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard | |||
|
152 | ||||
|
153 | .. _argparse: http://code.google.com/p/argparse/ | |||
|
154 | ||||
191 | Bug fixes |
|
155 | Bug fixes | |
192 | --------- |
|
156 | --------- | |
193 |
|
157 | |||
194 | * Fix #368719, removed top-level debian/ directory to make the job of Debian |
|
158 | * Fix #368719, removed top-level debian/ directory to make the job of Debian | |
195 | packagers easier. |
|
159 | packagers easier. | |
196 |
|
160 | |||
197 | * Fix #291143 by including man pages contributed by Stephan Peijnik from the |
|
161 | * Fix #291143 by including man pages contributed by Stephan Peijnik from the | |
198 | Debian project. |
|
162 | Debian project. | |
199 |
|
163 | |||
200 | * Fix #358202, effectively a race condition, by properly synchronizing file |
|
164 | * Fix #358202, effectively a race condition, by properly synchronizing file | |
201 | creation at cluster startup time. |
|
165 | creation at cluster startup time. | |
202 |
|
166 | |||
203 | * `%timeit` now handles correctly functions that take a long time to execute |
|
167 | * `%timeit` now handles correctly functions that take a long time to execute | |
204 | even the first time, by not repeating them. |
|
168 | even the first time, by not repeating them. | |
205 |
|
169 | |||
206 | * Fix #239054, releasing of references after exiting. |
|
170 | * Fix #239054, releasing of references after exiting. | |
207 |
|
171 | |||
208 | * Fix #341726, thanks to Alexander Clausen. |
|
172 | * Fix #341726, thanks to Alexander Clausen. | |
209 |
|
173 | |||
210 | * Fix #269966. This long-standing and very difficult bug (which is actually a |
|
174 | * Fix #269966. This long-standing and very difficult bug (which is actually a | |
211 | problem in Python itself) meant long-running sessions would inevitably grow |
|
175 | problem in Python itself) meant long-running sessions would inevitably grow | |
212 | in memory size, often with catastrophic consequences if users had large |
|
176 | in memory size, often with catastrophic consequences if users had large | |
213 | objects in their scripts. Now, using `%run` repeatedly should not cause any |
|
177 | objects in their scripts. Now, using `%run` repeatedly should not cause any | |
214 | memory leaks. Special thanks to John Hunter and Sameer D'Costa for their |
|
178 | memory leaks. Special thanks to John Hunter and Sameer D'Costa for their | |
215 | help with this bug. |
|
179 | help with this bug. | |
216 |
|
180 | |||
217 | * Fix #295371, bug in `%history`. |
|
181 | * Fix #295371, bug in `%history`. | |
218 |
|
182 | |||
219 | * Improved support for py2exe. |
|
183 | * Improved support for py2exe. | |
220 |
|
184 | |||
221 | * Fix #270856: IPython hangs with PyGTK |
|
185 | * Fix #270856: IPython hangs with PyGTK | |
222 |
|
186 | |||
223 | * Fix #270998: A magic with no docstring breaks the '%magic magic' |
|
187 | * Fix #270998: A magic with no docstring breaks the '%magic magic' | |
224 |
|
188 | |||
225 | * fix #271684: -c startup commands screw up raw vs. native history |
|
189 | * fix #271684: -c startup commands screw up raw vs. native history | |
226 |
|
190 | |||
227 | * Numerous bugs on Windows with the new ipcluster have been fixed. |
|
191 | * Numerous bugs on Windows with the new ipcluster have been fixed. | |
228 |
|
192 | |||
229 | * The ipengine and ipcontroller scripts now handle missing furl files |
|
193 | * The ipengine and ipcontroller scripts now handle missing furl files | |
230 | more gracefully by giving better error messages. |
|
194 | more gracefully by giving better error messages. | |
231 |
|
195 | |||
232 | * %rehashx: Aliases no longer contain dots. python3.0 binary |
|
196 | * %rehashx: Aliases no longer contain dots. python3.0 binary | |
233 | will create alias python30. Fixes: |
|
197 | will create alias python30. Fixes: | |
234 | #259716 "commands with dots in them don't work" |
|
198 | #259716 "commands with dots in them don't work" | |
235 |
|
199 | |||
236 | * %cpaste: %cpaste -r repeats the last pasted block. |
|
200 | * %cpaste: %cpaste -r repeats the last pasted block. | |
237 | The block is assigned to pasted_block even if code |
|
201 | The block is assigned to pasted_block even if code | |
238 | raises exception. |
|
202 | raises exception. | |
239 |
|
203 | |||
240 | * Bug #274067 'The code in get_home_dir is broken for py2exe' was |
|
204 | * Bug #274067 'The code in get_home_dir is broken for py2exe' was | |
241 | fixed. |
|
205 | fixed. | |
242 |
|
206 | |||
|
207 | * Many other small bug fixes not listed here by number (see the bzr log for | |||
|
208 | more info). | |||
|
209 | ||||
243 | Backwards incompatible changes |
|
210 | Backwards incompatible changes | |
244 | ------------------------------ |
|
211 | ------------------------------ | |
245 |
|
212 | |||
246 | * `ipykit` and related files were unmaintained and have been removed. |
|
213 | * `ipykit` and related files were unmaintained and have been removed. | |
247 |
|
214 | |||
248 | * The :func:`IPython.genutils.doctest_reload` does not actually call |
|
215 | * The :func:`IPython.genutils.doctest_reload` does not actually call | |
249 | `reload(doctest)` anymore, as this was causing many problems with the test |
|
216 | `reload(doctest)` anymore, as this was causing many problems with the test | |
250 | suite. It still resets `doctest.master` to None. |
|
217 | suite. It still resets `doctest.master` to None. | |
251 |
|
218 | |||
252 | * While we have not deliberately broken Python 2.4 compatibility, only minor |
|
219 | * While we have not deliberately broken Python 2.4 compatibility, only minor | |
253 | testing was done with Python 2.4, while 2.5 and 2.6 were fully tested. But |
|
220 | testing was done with Python 2.4, while 2.5 and 2.6 were fully tested. But | |
254 | if you encounter problems with 2.4, please do report them as bugs. |
|
221 | if you encounter problems with 2.4, please do report them as bugs. | |
255 |
|
222 | |||
256 | * The :command:`ipcluster` now requires a mode argument; for example to start a |
|
223 | * The :command:`ipcluster` now requires a mode argument; for example to start a | |
257 | cluster on the local machine with 4 engines, you must now type:: |
|
224 | cluster on the local machine with 4 engines, you must now type:: | |
258 |
|
225 | |||
259 | $ ipcluster local -n 4 |
|
226 | $ ipcluster local -n 4 | |
260 |
|
227 | |||
261 | * The controller now has a ``-r`` flag that needs to be used if you want to |
|
228 | * The controller now has a ``-r`` flag that needs to be used if you want to | |
262 | reuse existing furl files. Otherwise they are deleted (the default). |
|
229 | reuse existing furl files. Otherwise they are deleted (the default). | |
263 |
|
230 | |||
264 | * Remove ipy_leo.py. You can use :command:`easy_install ipython-extension` to |
|
231 | * Remove ipy_leo.py. You can use :command:`easy_install ipython-extension` to | |
265 | get it. (done to decouple it from ipython release cycle) |
|
232 | get it. (done to decouple it from ipython release cycle) | |
266 |
|
233 | |||
267 |
|
234 | |||
268 | Release 0.9.1 |
|
235 | Release 0.9.1 | |
269 | ============= |
|
236 | ============= | |
270 |
|
237 | |||
271 | This release was quickly made to restore compatibility with Python 2.4, which |
|
238 | This release was quickly made to restore compatibility with Python 2.4, which | |
272 | version 0.9 accidentally broke. No new features were introduced, other than |
|
239 | version 0.9 accidentally broke. No new features were introduced, other than | |
273 | some additional testing support for internal use. |
|
240 | some additional testing support for internal use. | |
274 |
|
241 | |||
275 |
|
242 | |||
276 | Release 0.9 |
|
243 | Release 0.9 | |
277 | =========== |
|
244 | =========== | |
278 |
|
245 | |||
279 | New features |
|
246 | New features | |
280 | ------------ |
|
247 | ------------ | |
281 |
|
248 | |||
282 | * All furl files and security certificates are now put in a read-only |
|
249 | * All furl files and security certificates are now put in a read-only | |
283 | directory named ~./ipython/security. |
|
250 | directory named ~./ipython/security. | |
284 |
|
251 | |||
285 | * A single function :func:`get_ipython_dir`, in :mod:`IPython.genutils` that |
|
252 | * A single function :func:`get_ipython_dir`, in :mod:`IPython.genutils` that | |
286 | determines the user's IPython directory in a robust manner. |
|
253 | determines the user's IPython directory in a robust manner. | |
287 |
|
254 | |||
288 | * Laurent's WX application has been given a top-level script called |
|
255 | * Laurent's WX application has been given a top-level script called | |
289 | ipython-wx, and it has received numerous fixes. We expect this code to be |
|
256 | ipython-wx, and it has received numerous fixes. We expect this code to be | |
290 | architecturally better integrated with Gael's WX 'ipython widget' over the |
|
257 | architecturally better integrated with Gael's WX 'ipython widget' over the | |
291 | next few releases. |
|
258 | next few releases. | |
292 |
|
259 | |||
293 | * The Editor synchronization work by Vivian De Smedt has been merged in. This |
|
260 | * The Editor synchronization work by Vivian De Smedt has been merged in. This | |
294 | code adds a number of new editor hooks to synchronize with editors under |
|
261 | code adds a number of new editor hooks to synchronize with editors under | |
295 | Windows. |
|
262 | Windows. | |
296 |
|
263 | |||
297 | * A new, still experimental but highly functional, WX shell by Gael Varoquaux. |
|
264 | * A new, still experimental but highly functional, WX shell by Gael Varoquaux. | |
298 | This work was sponsored by Enthought, and while it's still very new, it is |
|
265 | This work was sponsored by Enthought, and while it's still very new, it is | |
299 | based on a more cleanly organized arhictecture of the various IPython |
|
266 | based on a more cleanly organized arhictecture of the various IPython | |
300 | components. We will continue to develop this over the next few releases as a |
|
267 | components. We will continue to develop this over the next few releases as a | |
301 | model for GUI components that use IPython. |
|
268 | model for GUI components that use IPython. | |
302 |
|
269 | |||
303 | * Another GUI frontend, Cocoa based (Cocoa is the OSX native GUI framework), |
|
270 | * Another GUI frontend, Cocoa based (Cocoa is the OSX native GUI framework), | |
304 | authored by Barry Wark. Currently the WX and the Cocoa ones have slightly |
|
271 | authored by Barry Wark. Currently the WX and the Cocoa ones have slightly | |
305 | different internal organizations, but the whole team is working on finding |
|
272 | different internal organizations, but the whole team is working on finding | |
306 | what the right abstraction points are for a unified codebase. |
|
273 | what the right abstraction points are for a unified codebase. | |
307 |
|
274 | |||
308 | * As part of the frontend work, Barry Wark also implemented an experimental |
|
275 | * As part of the frontend work, Barry Wark also implemented an experimental | |
309 | event notification system that various ipython components can use. In the |
|
276 | event notification system that various ipython components can use. In the | |
310 | next release the implications and use patterns of this system regarding the |
|
277 | next release the implications and use patterns of this system regarding the | |
311 | various GUI options will be worked out. |
|
278 | various GUI options will be worked out. | |
312 |
|
279 | |||
313 | * IPython finally has a full test system, that can test docstrings with |
|
280 | * IPython finally has a full test system, that can test docstrings with | |
314 | IPython-specific functionality. There are still a few pieces missing for it |
|
281 | IPython-specific functionality. There are still a few pieces missing for it | |
315 | to be widely accessible to all users (so they can run the test suite at any |
|
282 | to be widely accessible to all users (so they can run the test suite at any | |
316 | time and report problems), but it now works for the developers. We are |
|
283 | time and report problems), but it now works for the developers. We are | |
317 | working hard on continuing to improve it, as this was probably IPython's |
|
284 | working hard on continuing to improve it, as this was probably IPython's | |
318 | major Achilles heel (the lack of proper test coverage made it effectively |
|
285 | major Achilles heel (the lack of proper test coverage made it effectively | |
319 | impossible to do large-scale refactoring). The full test suite can now |
|
286 | impossible to do large-scale refactoring). The full test suite can now | |
320 | be run using the :command:`iptest` command line program. |
|
287 | be run using the :command:`iptest` command line program. | |
321 |
|
288 | |||
322 | * The notion of a task has been completely reworked. An `ITask` interface has |
|
289 | * The notion of a task has been completely reworked. An `ITask` interface has | |
323 | been created. This interface defines the methods that tasks need to |
|
290 | been created. This interface defines the methods that tasks need to | |
324 | implement. These methods are now responsible for things like submitting |
|
291 | implement. These methods are now responsible for things like submitting | |
325 | tasks and processing results. There are two basic task types: |
|
292 | tasks and processing results. There are two basic task types: | |
326 | :class:`IPython.kernel.task.StringTask` (this is the old `Task` object, but |
|
293 | :class:`IPython.kernel.task.StringTask` (this is the old `Task` object, but | |
327 | renamed) and the new :class:`IPython.kernel.task.MapTask`, which is based on |
|
294 | renamed) and the new :class:`IPython.kernel.task.MapTask`, which is based on | |
328 | a function. |
|
295 | a function. | |
329 |
|
296 | |||
330 | * A new interface, :class:`IPython.kernel.mapper.IMapper` has been defined to |
|
297 | * A new interface, :class:`IPython.kernel.mapper.IMapper` has been defined to | |
331 | standardize the idea of a `map` method. This interface has a single `map` |
|
298 | standardize the idea of a `map` method. This interface has a single `map` | |
332 | method that has the same syntax as the built-in `map`. We have also defined |
|
299 | method that has the same syntax as the built-in `map`. We have also defined | |
333 | a `mapper` factory interface that creates objects that implement |
|
300 | a `mapper` factory interface that creates objects that implement | |
334 | :class:`IPython.kernel.mapper.IMapper` for different controllers. Both the |
|
301 | :class:`IPython.kernel.mapper.IMapper` for different controllers. Both the | |
335 | multiengine and task controller now have mapping capabilties. |
|
302 | multiengine and task controller now have mapping capabilties. | |
336 |
|
303 | |||
337 | * The parallel function capabilities have been reworks. The major changes are |
|
304 | * The parallel function capabilities have been reworks. The major changes are | |
338 | that i) there is now an `@parallel` magic that creates parallel functions, |
|
305 | that i) there is now an `@parallel` magic that creates parallel functions, | |
339 | ii) the syntax for mulitple variable follows that of `map`, iii) both the |
|
306 | ii) the syntax for mulitple variable follows that of `map`, iii) both the | |
340 | multiengine and task controller now have a parallel function implementation. |
|
307 | multiengine and task controller now have a parallel function implementation. | |
341 |
|
308 | |||
342 | * All of the parallel computing capabilities from `ipython1-dev` have been |
|
309 | * All of the parallel computing capabilities from `ipython1-dev` have been | |
343 | merged into IPython proper. This resulted in the following new subpackages: |
|
310 | merged into IPython proper. This resulted in the following new subpackages: | |
344 | :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`, |
|
311 | :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`IPython.config`, | |
345 | :mod:`IPython.tools` and :mod:`IPython.testing`. |
|
312 | :mod:`IPython.tools` and :mod:`IPython.testing`. | |
346 |
|
313 | |||
347 | * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and |
|
314 | * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and | |
348 | friends have been completely refactored. Now we are checking for |
|
315 | friends have been completely refactored. Now we are checking for | |
349 | dependencies using the approach that matplotlib uses. |
|
316 | dependencies using the approach that matplotlib uses. | |
350 |
|
317 | |||
351 | * The documentation has been completely reorganized to accept the |
|
318 | * The documentation has been completely reorganized to accept the | |
352 | documentation from `ipython1-dev`. |
|
319 | documentation from `ipython1-dev`. | |
353 |
|
320 | |||
354 | * We have switched to using Foolscap for all of our network protocols in |
|
321 | * We have switched to using Foolscap for all of our network protocols in | |
355 | :mod:`IPython.kernel`. This gives us secure connections that are both |
|
322 | :mod:`IPython.kernel`. This gives us secure connections that are both | |
356 | encrypted and authenticated. |
|
323 | encrypted and authenticated. | |
357 |
|
324 | |||
358 | * We have a brand new `COPYING.txt` files that describes the IPython license |
|
325 | * We have a brand new `COPYING.txt` files that describes the IPython license | |
359 | and copyright. The biggest change is that we are putting "The IPython |
|
326 | and copyright. The biggest change is that we are putting "The IPython | |
360 | Development Team" as the copyright holder. We give more details about |
|
327 | Development Team" as the copyright holder. We give more details about | |
361 | exactly what this means in this file. All developer should read this and use |
|
328 | exactly what this means in this file. All developer should read this and use | |
362 | the new banner in all IPython source code files. |
|
329 | the new banner in all IPython source code files. | |
363 |
|
330 | |||
364 | * sh profile: ./foo runs foo as system command, no need to do !./foo anymore |
|
331 | * sh profile: ./foo runs foo as system command, no need to do !./foo anymore | |
365 |
|
332 | |||
366 | * String lists now support ``sort(field, nums = True)`` method (to easily sort |
|
333 | * String lists now support ``sort(field, nums = True)`` method (to easily sort | |
367 | system command output). Try it with ``a = !ls -l ; a.sort(1, nums=1)``. |
|
334 | system command output). Try it with ``a = !ls -l ; a.sort(1, nums=1)``. | |
368 |
|
335 | |||
369 | * '%cpaste foo' now assigns the pasted block as string list, instead of string |
|
336 | * '%cpaste foo' now assigns the pasted block as string list, instead of string | |
370 |
|
337 | |||
371 | * The ipcluster script now run by default with no security. This is done |
|
338 | * The ipcluster script now run by default with no security. This is done | |
372 | because the main usage of the script is for starting things on localhost. |
|
339 | because the main usage of the script is for starting things on localhost. | |
373 | Eventually when ipcluster is able to start things on other hosts, we will put |
|
340 | Eventually when ipcluster is able to start things on other hosts, we will put | |
374 | security back. |
|
341 | security back. | |
375 |
|
342 | |||
376 | * 'cd --foo' searches directory history for string foo, and jumps to that dir. |
|
343 | * 'cd --foo' searches directory history for string foo, and jumps to that dir. | |
377 | Last part of dir name is checked first. If no matches for that are found, |
|
344 | Last part of dir name is checked first. If no matches for that are found, | |
378 | look at the whole path. |
|
345 | look at the whole path. | |
379 |
|
346 | |||
380 |
|
347 | |||
381 | Bug fixes |
|
348 | Bug fixes | |
382 | --------- |
|
349 | --------- | |
383 |
|
350 | |||
384 | * The Windows installer has been fixed. Now all IPython scripts have ``.bat`` |
|
351 | * The Windows installer has been fixed. Now all IPython scripts have ``.bat`` | |
385 | versions created. Also, the Start Menu shortcuts have been updated. |
|
352 | versions created. Also, the Start Menu shortcuts have been updated. | |
386 |
|
353 | |||
387 | * The colors escapes in the multiengine client are now turned off on win32 as |
|
354 | * The colors escapes in the multiengine client are now turned off on win32 as | |
388 | they don't print correctly. |
|
355 | they don't print correctly. | |
389 |
|
356 | |||
390 | * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing |
|
357 | * The :mod:`IPython.kernel.scripts.ipengine` script was exec'ing | |
391 | mpi_import_statement incorrectly, which was leading the engine to crash when |
|
358 | mpi_import_statement incorrectly, which was leading the engine to crash when | |
392 | mpi was enabled. |
|
359 | mpi was enabled. | |
393 |
|
360 | |||
394 | * A few subpackages had missing ``__init__.py`` files. |
|
361 | * A few subpackages had missing ``__init__.py`` files. | |
395 |
|
362 | |||
396 | * The documentation is only created if Sphinx is found. Previously, the |
|
363 | * The documentation is only created if Sphinx is found. Previously, the | |
397 | ``setup.py`` script would fail if it was missing. |
|
364 | ``setup.py`` script would fail if it was missing. | |
398 |
|
365 | |||
399 | * Greedy ``cd`` completion has been disabled again (it was enabled in 0.8.4) as |
|
366 | * Greedy ``cd`` completion has been disabled again (it was enabled in 0.8.4) as | |
400 | it caused problems on certain platforms. |
|
367 | it caused problems on certain platforms. | |
401 |
|
368 | |||
402 |
|
369 | |||
403 | Backwards incompatible changes |
|
370 | Backwards incompatible changes | |
404 | ------------------------------ |
|
371 | ------------------------------ | |
405 |
|
372 | |||
406 | * The ``clusterfile`` options of the :command:`ipcluster` command has been |
|
373 | * The ``clusterfile`` options of the :command:`ipcluster` command has been | |
407 | removed as it was not working and it will be replaced soon by something much |
|
374 | removed as it was not working and it will be replaced soon by something much | |
408 | more robust. |
|
375 | more robust. | |
409 |
|
376 | |||
410 | * The :mod:`IPython.kernel` configuration now properly find the user's |
|
377 | * The :mod:`IPython.kernel` configuration now properly find the user's | |
411 | IPython directory. |
|
378 | IPython directory. | |
412 |
|
379 | |||
413 | * In ipapi, the :func:`make_user_ns` function has been replaced with |
|
380 | * In ipapi, the :func:`make_user_ns` function has been replaced with | |
414 | :func:`make_user_namespaces`, to support dict subclasses in namespace |
|
381 | :func:`make_user_namespaces`, to support dict subclasses in namespace | |
415 | creation. |
|
382 | creation. | |
416 |
|
383 | |||
417 | * :class:`IPython.kernel.client.Task` has been renamed |
|
384 | * :class:`IPython.kernel.client.Task` has been renamed | |
418 | :class:`IPython.kernel.client.StringTask` to make way for new task types. |
|
385 | :class:`IPython.kernel.client.StringTask` to make way for new task types. | |
419 |
|
386 | |||
420 | * The keyword argument `style` has been renamed `dist` in `scatter`, `gather` |
|
387 | * The keyword argument `style` has been renamed `dist` in `scatter`, `gather` | |
421 | and `map`. |
|
388 | and `map`. | |
422 |
|
389 | |||
423 | * Renamed the values that the rename `dist` keyword argument can have from |
|
390 | * Renamed the values that the rename `dist` keyword argument can have from | |
424 | `'basic'` to `'b'`. |
|
391 | `'basic'` to `'b'`. | |
425 |
|
392 | |||
426 | * IPython has a larger set of dependencies if you want all of its capabilities. |
|
393 | * IPython has a larger set of dependencies if you want all of its capabilities. | |
427 | See the `setup.py` script for details. |
|
394 | See the `setup.py` script for details. | |
428 |
|
395 | |||
429 | * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and |
|
396 | * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and | |
430 | :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple. |
|
397 | :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple. | |
431 | Instead they take the filename of a file that contains the FURL for that |
|
398 | Instead they take the filename of a file that contains the FURL for that | |
432 | client. If the FURL file is in your IPYTHONDIR, it will be found automatically |
|
399 | client. If the FURL file is in your IPYTHONDIR, it will be found automatically | |
433 | and the constructor can be left empty. |
|
400 | and the constructor can be left empty. | |
434 |
|
401 | |||
435 | * The asynchronous clients in :mod:`IPython.kernel.asyncclient` are now created |
|
402 | * The asynchronous clients in :mod:`IPython.kernel.asyncclient` are now created | |
436 | using the factory functions :func:`get_multiengine_client` and |
|
403 | using the factory functions :func:`get_multiengine_client` and | |
437 | :func:`get_task_client`. These return a `Deferred` to the actual client. |
|
404 | :func:`get_task_client`. These return a `Deferred` to the actual client. | |
438 |
|
405 | |||
439 | * The command line options to `ipcontroller` and `ipengine` have changed to |
|
406 | * The command line options to `ipcontroller` and `ipengine` have changed to | |
440 | reflect the new Foolscap network protocol and the FURL files. Please see the |
|
407 | reflect the new Foolscap network protocol and the FURL files. Please see the | |
441 | help for these scripts for details. |
|
408 | help for these scripts for details. | |
442 |
|
409 | |||
443 | * The configuration files for the kernel have changed because of the Foolscap |
|
410 | * The configuration files for the kernel have changed because of the Foolscap | |
444 | stuff. If you were using custom config files before, you should delete them |
|
411 | stuff. If you were using custom config files before, you should delete them | |
445 | and regenerate new ones. |
|
412 | and regenerate new ones. | |
446 |
|
413 | |||
447 | Changes merged in from IPython1 |
|
414 | Changes merged in from IPython1 | |
448 | ------------------------------- |
|
415 | ------------------------------- | |
449 |
|
416 | |||
450 | New features |
|
417 | New features | |
451 | ............ |
|
418 | ............ | |
452 |
|
419 | |||
453 | * Much improved ``setup.py`` and ``setupegg.py`` scripts. Because Twisted and |
|
420 | * Much improved ``setup.py`` and ``setupegg.py`` scripts. Because Twisted and | |
454 | zope.interface are now easy installable, we can declare them as dependencies |
|
421 | zope.interface are now easy installable, we can declare them as dependencies | |
455 | in our setupegg.py script. |
|
422 | in our setupegg.py script. | |
456 |
|
423 | |||
457 | * IPython is now compatible with Twisted 2.5.0 and 8.x. |
|
424 | * IPython is now compatible with Twisted 2.5.0 and 8.x. | |
458 |
|
425 | |||
459 | * Added a new example of how to use :mod:`ipython1.kernel.asynclient`. |
|
426 | * Added a new example of how to use :mod:`ipython1.kernel.asynclient`. | |
460 |
|
427 | |||
461 | * Initial draft of a process daemon in :mod:`ipython1.daemon`. This has not |
|
428 | * Initial draft of a process daemon in :mod:`ipython1.daemon`. This has not | |
462 | been merged into IPython and is still in `ipython1-dev`. |
|
429 | been merged into IPython and is still in `ipython1-dev`. | |
463 |
|
430 | |||
464 | * The ``TaskController`` now has methods for getting the queue status. |
|
431 | * The ``TaskController`` now has methods for getting the queue status. | |
465 |
|
432 | |||
466 | * The ``TaskResult`` objects not have information about how long the task |
|
433 | * The ``TaskResult`` objects not have information about how long the task | |
467 | took to run. |
|
434 | took to run. | |
468 |
|
435 | |||
469 | * We are attaching additional attributes to exceptions ``(_ipython_*)`` that |
|
436 | * We are attaching additional attributes to exceptions ``(_ipython_*)`` that | |
470 | we use to carry additional info around. |
|
437 | we use to carry additional info around. | |
471 |
|
438 | |||
472 | * New top-level module :mod:`asyncclient` that has asynchronous versions (that |
|
439 | * New top-level module :mod:`asyncclient` that has asynchronous versions (that | |
473 | return deferreds) of the client classes. This is designed to users who want |
|
440 | return deferreds) of the client classes. This is designed to users who want | |
474 | to run their own Twisted reactor. |
|
441 | to run their own Twisted reactor. | |
475 |
|
442 | |||
476 | * All the clients in :mod:`client` are now based on Twisted. This is done by |
|
443 | * All the clients in :mod:`client` are now based on Twisted. This is done by | |
477 | running the Twisted reactor in a separate thread and using the |
|
444 | running the Twisted reactor in a separate thread and using the | |
478 | :func:`blockingCallFromThread` function that is in recent versions of Twisted. |
|
445 | :func:`blockingCallFromThread` function that is in recent versions of Twisted. | |
479 |
|
446 | |||
480 | * Functions can now be pushed/pulled to/from engines using |
|
447 | * Functions can now be pushed/pulled to/from engines using | |
481 | :meth:`MultiEngineClient.push_function` and |
|
448 | :meth:`MultiEngineClient.push_function` and | |
482 | :meth:`MultiEngineClient.pull_function`. |
|
449 | :meth:`MultiEngineClient.pull_function`. | |
483 |
|
450 | |||
484 | * Gather/scatter are now implemented in the client to reduce the work load |
|
451 | * Gather/scatter are now implemented in the client to reduce the work load | |
485 | of the controller and improve performance. |
|
452 | of the controller and improve performance. | |
486 |
|
453 | |||
487 | * Complete rewrite of the IPython docuementation. All of the documentation |
|
454 | * Complete rewrite of the IPython docuementation. All of the documentation | |
488 | from the IPython website has been moved into docs/source as restructured |
|
455 | from the IPython website has been moved into docs/source as restructured | |
489 | text documents. PDF and HTML documentation are being generated using |
|
456 | text documents. PDF and HTML documentation are being generated using | |
490 | Sphinx. |
|
457 | Sphinx. | |
491 |
|
458 | |||
492 | * New developer oriented documentation: development guidelines and roadmap. |
|
459 | * New developer oriented documentation: development guidelines and roadmap. | |
493 |
|
460 | |||
494 | * Traditional ``ChangeLog`` has been changed to a more useful ``changes.txt`` |
|
461 | * Traditional ``ChangeLog`` has been changed to a more useful ``changes.txt`` | |
495 | file that is organized by release and is meant to provide something more |
|
462 | file that is organized by release and is meant to provide something more | |
496 | relevant for users. |
|
463 | relevant for users. | |
497 |
|
464 | |||
498 | Bug fixes |
|
465 | Bug fixes | |
499 | ......... |
|
466 | ......... | |
500 |
|
467 | |||
501 | * Created a proper ``MANIFEST.in`` file to create source distributions. |
|
468 | * Created a proper ``MANIFEST.in`` file to create source distributions. | |
502 |
|
469 | |||
503 | * Fixed a bug in the ``MultiEngine`` interface. Previously, multi-engine |
|
470 | * Fixed a bug in the ``MultiEngine`` interface. Previously, multi-engine | |
504 | actions were being collected with a :class:`DeferredList` with |
|
471 | actions were being collected with a :class:`DeferredList` with | |
505 | ``fireononeerrback=1``. This meant that methods were returning |
|
472 | ``fireononeerrback=1``. This meant that methods were returning | |
506 | before all engines had given their results. This was causing extremely odd |
|
473 | before all engines had given their results. This was causing extremely odd | |
507 | bugs in certain cases. To fix this problem, we have 1) set |
|
474 | bugs in certain cases. To fix this problem, we have 1) set | |
508 | ``fireononeerrback=0`` to make sure all results (or exceptions) are in |
|
475 | ``fireononeerrback=0`` to make sure all results (or exceptions) are in | |
509 | before returning and 2) introduced a :exc:`CompositeError` exception |
|
476 | before returning and 2) introduced a :exc:`CompositeError` exception | |
510 | that wraps all of the engine exceptions. This is a huge change as it means |
|
477 | that wraps all of the engine exceptions. This is a huge change as it means | |
511 | that users will have to catch :exc:`CompositeError` rather than the actual |
|
478 | that users will have to catch :exc:`CompositeError` rather than the actual | |
512 | exception. |
|
479 | exception. | |
513 |
|
480 | |||
514 | Backwards incompatible changes |
|
481 | Backwards incompatible changes | |
515 | .............................. |
|
482 | .............................. | |
516 |
|
483 | |||
517 | * All names have been renamed to conform to the lowercase_with_underscore |
|
484 | * All names have been renamed to conform to the lowercase_with_underscore | |
518 | convention. This will require users to change references to all names like |
|
485 | convention. This will require users to change references to all names like | |
519 | ``queueStatus`` to ``queue_status``. |
|
486 | ``queueStatus`` to ``queue_status``. | |
520 |
|
487 | |||
521 | * Previously, methods like :meth:`MultiEngineClient.push` and |
|
488 | * Previously, methods like :meth:`MultiEngineClient.push` and | |
522 | :meth:`MultiEngineClient.push` used ``*args`` and ``**kwargs``. This was |
|
489 | :meth:`MultiEngineClient.push` used ``*args`` and ``**kwargs``. This was | |
523 | becoming a problem as we weren't able to introduce new keyword arguments into |
|
490 | becoming a problem as we weren't able to introduce new keyword arguments into | |
524 | the API. Now these methods simple take a dict or sequence. This has also |
|
491 | the API. Now these methods simple take a dict or sequence. This has also | |
525 | allowed us to get rid of the ``*All`` methods like :meth:`pushAll` and |
|
492 | allowed us to get rid of the ``*All`` methods like :meth:`pushAll` and | |
526 | :meth:`pullAll`. These things are now handled with the ``targets`` keyword |
|
493 | :meth:`pullAll`. These things are now handled with the ``targets`` keyword | |
527 | argument that defaults to ``'all'``. |
|
494 | argument that defaults to ``'all'``. | |
528 |
|
495 | |||
529 | * The :attr:`MultiEngineClient.magicTargets` has been renamed to |
|
496 | * The :attr:`MultiEngineClient.magicTargets` has been renamed to | |
530 | :attr:`MultiEngineClient.targets`. |
|
497 | :attr:`MultiEngineClient.targets`. | |
531 |
|
498 | |||
532 | * All methods in the MultiEngine interface now accept the optional keyword |
|
499 | * All methods in the MultiEngine interface now accept the optional keyword | |
533 | argument ``block``. |
|
500 | argument ``block``. | |
534 |
|
501 | |||
535 | * Renamed :class:`RemoteController` to :class:`MultiEngineClient` and |
|
502 | * Renamed :class:`RemoteController` to :class:`MultiEngineClient` and | |
536 | :class:`TaskController` to :class:`TaskClient`. |
|
503 | :class:`TaskController` to :class:`TaskClient`. | |
537 |
|
504 | |||
538 | * Renamed the top-level module from :mod:`api` to :mod:`client`. |
|
505 | * Renamed the top-level module from :mod:`api` to :mod:`client`. | |
539 |
|
506 | |||
540 | * Most methods in the multiengine interface now raise a :exc:`CompositeError` |
|
507 | * Most methods in the multiengine interface now raise a :exc:`CompositeError` | |
541 | exception that wraps the user's exceptions, rather than just raising the raw |
|
508 | exception that wraps the user's exceptions, rather than just raising the raw | |
542 | user's exception. |
|
509 | user's exception. | |
543 |
|
510 | |||
544 | * Changed the ``setupNS`` and ``resultNames`` in the ``Task`` class to ``push`` |
|
511 | * Changed the ``setupNS`` and ``resultNames`` in the ``Task`` class to ``push`` | |
545 | and ``pull``. |
|
512 | and ``pull``. | |
546 |
|
513 | |||
547 |
|
514 | |||
548 | Release 0.8.4 |
|
515 | Release 0.8.4 | |
549 | ============= |
|
516 | ============= | |
550 |
|
517 | |||
551 | This was a quick release to fix an unfortunate bug that slipped into the 0.8.3 |
|
518 | This was a quick release to fix an unfortunate bug that slipped into the 0.8.3 | |
552 | release. The ``--twisted`` option was disabled, as it turned out to be broken |
|
519 | release. The ``--twisted`` option was disabled, as it turned out to be broken | |
553 | across several platforms. |
|
520 | across several platforms. | |
554 |
|
521 | |||
555 |
|
522 | |||
556 | Release 0.8.3 |
|
523 | Release 0.8.3 | |
557 | ============= |
|
524 | ============= | |
558 |
|
525 | |||
559 | * pydb is now disabled by default (due to %run -d problems). You can enable |
|
526 | * pydb is now disabled by default (due to %run -d problems). You can enable | |
560 | it by passing -pydb command line argument to IPython. Note that setting |
|
527 | it by passing -pydb command line argument to IPython. Note that setting | |
561 | it in config file won't work. |
|
528 | it in config file won't work. | |
562 |
|
529 | |||
563 |
|
530 | |||
564 | Release 0.8.2 |
|
531 | Release 0.8.2 | |
565 | ============= |
|
532 | ============= | |
566 |
|
533 | |||
567 | * %pushd/%popd behave differently; now "pushd /foo" pushes CURRENT directory |
|
534 | * %pushd/%popd behave differently; now "pushd /foo" pushes CURRENT directory | |
568 | and jumps to /foo. The current behaviour is closer to the documented |
|
535 | and jumps to /foo. The current behaviour is closer to the documented | |
569 | behaviour, and should not trip anyone. |
|
536 | behaviour, and should not trip anyone. | |
570 |
|
537 | |||
571 |
|
538 | |||
572 | Older releases |
|
539 | Older releases | |
573 | ============== |
|
540 | ============== | |
574 |
|
541 | |||
575 | Changes in earlier releases of IPython are described in the older file |
|
542 | Changes in earlier releases of IPython are described in the older file | |
576 | ``ChangeLog``. Please refer to this document for details. |
|
543 | ``ChangeLog``. Please refer to this document for details. | |
577 |
|
544 |
@@ -1,494 +1,515 b'' | |||||
1 | .. _development: |
|
1 | .. _development: | |
2 |
|
2 | |||
3 | ============================== |
|
3 | ============================== | |
4 | IPython development guidelines |
|
4 | IPython development guidelines | |
5 | ============================== |
|
5 | ============================== | |
6 |
|
6 | |||
7 |
|
7 | |||
8 | Overview |
|
8 | Overview | |
9 | ======== |
|
9 | ======== | |
10 |
|
10 | |||
11 | This document describes IPython from the perspective of developers. Most |
|
11 | This document describes IPython from the perspective of developers. Most | |
12 | importantly, it gives information for people who want to contribute to the |
|
12 | importantly, it gives information for people who want to contribute to the | |
13 | development of IPython. So if you want to help out, read on! |
|
13 | development of IPython. So if you want to help out, read on! | |
14 |
|
14 | |||
15 | How to contribute to IPython |
|
15 | How to contribute to IPython | |
16 | ============================ |
|
16 | ============================ | |
17 |
|
17 | |||
18 | IPython development is done using Bazaar [Bazaar]_ and Launchpad [Launchpad]_. |
|
18 | IPython development is done using Bazaar [Bazaar]_ and Launchpad [Launchpad]_. | |
19 | This makes it easy for people to contribute to the development of IPython. |
|
19 | This makes it easy for people to contribute to the development of IPython. | |
20 | There are several ways in which you can join in. |
|
20 | There are several ways in which you can join in. | |
21 |
|
21 | |||
22 | If you have a small change that you want to send to the team, you can edit your |
|
22 | If you have a small change that you want to send to the team, you can edit your | |
23 | bazaar checkout of IPython (see below) in-place, and ask bazaar for the |
|
23 | bazaar checkout of IPython (see below) in-place, and ask bazaar for the | |
24 | differences:: |
|
24 | differences:: | |
25 |
|
25 | |||
26 | $ cd /path/to/your/copy/of/ipython |
|
26 | $ cd /path/to/your/copy/of/ipython | |
27 | $ bzr diff > my_fixes.diff |
|
27 | $ bzr diff > my_fixes.diff | |
28 |
|
28 | |||
29 | This produces a patch file with your fixes, which we can apply to the source |
|
29 | This produces a patch file with your fixes, which we can apply to the source | |
30 | tree. This file should then be attached to a ticket in our `bug tracker |
|
30 | tree. This file should then be attached to a ticket in our `bug tracker | |
31 | <https://bugs.launchpad.net/ipython>`_, indicating what it does. |
|
31 | <https://bugs.launchpad.net/ipython>`_, indicating what it does. | |
32 |
|
32 | |||
33 | This model of creating small, self-contained patches works very well and there |
|
33 | This model of creating small, self-contained patches works very well and there | |
34 | are open source projects that do their entire development this way. However, |
|
34 | are open source projects that do their entire development this way. However, | |
35 | in IPython we have found that for tracking larger changes, making use of |
|
35 | in IPython we have found that for tracking larger changes, making use of | |
36 | bazaar's full capabilities in conjunction with Launchpad's code hosting |
|
36 | bazaar's full capabilities in conjunction with Launchpad's code hosting | |
37 | services makes for a much better experience. |
|
37 | services makes for a much better experience. | |
38 |
|
38 | |||
39 | Making your own branch of IPython allows you to refine your changes over time, |
|
39 | Making your own branch of IPython allows you to refine your changes over time, | |
40 | track the development of the main team, and propose your own full version of |
|
40 | track the development of the main team, and propose your own full version of | |
41 | the code for others to use and review, with a minimum amount of fuss. The next |
|
41 | the code for others to use and review, with a minimum amount of fuss. The next | |
42 | parts of this document will explain how to do this. |
|
42 | parts of this document will explain how to do this. | |
43 |
|
43 | |||
44 | Install Bazaar and create a Launchpad account |
|
44 | Install Bazaar and create a Launchpad account | |
45 | --------------------------------------------- |
|
45 | --------------------------------------------- | |
46 |
|
46 | |||
47 | First make sure you have installed Bazaar (see their `website |
|
47 | First make sure you have installed Bazaar (see their `website | |
48 | <http://bazaar-vcs.org/>`_). To see that Bazaar is installed and knows about |
|
48 | <http://bazaar-vcs.org/>`_). To see that Bazaar is installed and knows about | |
49 | you, try the following:: |
|
49 | you, try the following:: | |
50 |
|
50 | |||
51 | $ bzr whoami |
|
51 | $ bzr whoami | |
52 | Joe Coder <jcoder@gmail.com> |
|
52 | Joe Coder <jcoder@gmail.com> | |
53 |
|
53 | |||
54 | This should display your name and email. Next, you will want to create an |
|
54 | This should display your name and email. Next, you will want to create an | |
55 | account on the `Launchpad website <http://www.launchpad.net>`_ and setup your |
|
55 | account on the `Launchpad website <http://www.launchpad.net>`_ and setup your | |
56 | ssh keys. For more information of setting up your ssh keys, see `this link |
|
56 | ssh keys. For more information of setting up your ssh keys, see `this link | |
57 | <https://help.launchpad.net/YourAccount/CreatingAnSSHKeyPair>`_. |
|
57 | <https://help.launchpad.net/YourAccount/CreatingAnSSHKeyPair>`_. | |
58 |
|
58 | |||
59 | Get the main IPython branch from Launchpad |
|
59 | Get the main IPython branch from Launchpad | |
60 | ------------------------------------------ |
|
60 | ------------------------------------------ | |
61 |
|
61 | |||
62 | Now, you can get a copy of the main IPython development branch (we call this |
|
62 | Now, you can get a copy of the main IPython development branch (we call this | |
63 | the "trunk"):: |
|
63 | the "trunk"):: | |
64 |
|
64 | |||
65 | $ bzr branch lp:ipython |
|
65 | $ bzr branch lp:ipython | |
66 |
|
66 | |||
67 | Create a working branch |
|
67 | Create a working branch | |
68 | ----------------------- |
|
68 | ----------------------- | |
69 |
|
69 | |||
70 | When working on IPython, you won't actually make edits directly to the |
|
70 | When working on IPython, you won't actually make edits directly to the | |
71 | :file:`lp:ipython` branch. Instead, you will create a separate branch for your |
|
71 | :file:`lp:ipython` branch. Instead, you will create a separate branch for your | |
72 | changes. For now, let's assume you want to do your work in a branch named |
|
72 | changes. For now, let's assume you want to do your work in a branch named | |
73 | "ipython-mybranch". Create this branch by doing:: |
|
73 | "ipython-mybranch". Create this branch by doing:: | |
74 |
|
74 | |||
75 | $ bzr branch ipython ipython-mybranch |
|
75 | $ bzr branch ipython ipython-mybranch | |
76 |
|
76 | |||
77 | When you actually create a branch, you will want to give it a name that |
|
77 | When you actually create a branch, you will want to give it a name that | |
78 | reflects the nature of the work that you will be doing in it, like |
|
78 | reflects the nature of the work that you will be doing in it, like | |
79 | "install-docs-update". |
|
79 | "install-docs-update". | |
80 |
|
80 | |||
81 | Make edits in your working branch |
|
81 | Make edits in your working branch | |
82 | --------------------------------- |
|
82 | --------------------------------- | |
83 |
|
83 | |||
84 | Now you are ready to actually make edits in your :file:`ipython-mybranch` |
|
84 | Now you are ready to actually make edits in your :file:`ipython-mybranch` | |
85 | branch. Before doing this, it is helpful to install this branch so you can |
|
85 | branch. Before doing this, it is helpful to install this branch so you can | |
86 | test your changes as you work. This is easiest if you have setuptools |
|
86 | test your changes as you work. This is easiest if you have setuptools | |
87 | installed. Then, just do:: |
|
87 | installed. Then, just do:: | |
88 |
|
88 | |||
89 | $ cd ipython-mybranch |
|
89 | $ cd ipython-mybranch | |
90 | $ python setupegg.py develop |
|
90 | $ python setupegg.py develop | |
91 |
|
91 | |||
92 | Now, make some changes. After a while, you will want to commit your changes. |
|
92 | Now, make some changes. After a while, you will want to commit your changes. | |
93 | This let's Bazaar know that you like the changes you have made and gives you |
|
93 | This let's Bazaar know that you like the changes you have made and gives you | |
94 | an opportunity to keep a nice record of what you have done. This looks like |
|
94 | an opportunity to keep a nice record of what you have done. This looks like | |
95 | this:: |
|
95 | this:: | |
96 |
|
96 | |||
97 | $ ...do work in ipython-mybranch... |
|
97 | $ ...do work in ipython-mybranch... | |
98 | $ bzr commit -m "the commit message goes here" |
|
98 | $ bzr commit -m "the commit message goes here" | |
99 |
|
99 | |||
100 | Please note that since we now don't use an old-style linear ChangeLog (that |
|
100 | Please note that since we now don't use an old-style linear ChangeLog (that | |
101 | tends to cause problems with distributed version control systems), you should |
|
101 | tends to cause problems with distributed version control systems), you should | |
102 | ensure that your log messages are reasonably detailed. Use a docstring-like |
|
102 | ensure that your log messages are reasonably detailed. Use a docstring-like | |
103 | approach in the commit messages (including the second line being left |
|
103 | approach in the commit messages (including the second line being left | |
104 | *blank*):: |
|
104 | *blank*):: | |
105 |
|
105 | |||
106 | Single line summary of changes being committed. |
|
106 | Single line summary of changes being committed. | |
107 |
|
107 | |||
108 | * more details when warranted ... |
|
108 | * more details when warranted ... | |
109 | * including crediting outside contributors if they sent the |
|
109 | * including crediting outside contributors if they sent the | |
110 | code/bug/idea! |
|
110 | code/bug/idea! | |
111 |
|
111 | |||
112 | As you work, you will repeat this edit/commit cycle many times. If you work on |
|
112 | As you work, you will repeat this edit/commit cycle many times. If you work on | |
113 | your branch for a long time, you will also want to get the latest changes from |
|
113 | your branch for a long time, you will also want to get the latest changes from | |
114 | the :file:`lp:ipython` branch. This can be done with the following sequence of |
|
114 | the :file:`lp:ipython` branch. This can be done with the following sequence of | |
115 | commands:: |
|
115 | commands:: | |
116 |
|
116 | |||
117 | $ ls |
|
117 | $ ls | |
118 | ipython |
|
118 | ipython | |
119 | ipython-mybranch |
|
119 | ipython-mybranch | |
120 |
|
120 | |||
121 | $ cd ipython |
|
121 | $ cd ipython | |
122 | $ bzr pull |
|
122 | $ bzr pull | |
123 | $ cd ../ipython-mybranch |
|
123 | $ cd ../ipython-mybranch | |
124 | $ bzr merge ../ipython |
|
124 | $ bzr merge ../ipython | |
125 | $ bzr commit -m "Merging changes from trunk" |
|
125 | $ bzr commit -m "Merging changes from trunk" | |
126 |
|
126 | |||
127 | Along the way, you should also run the IPython test suite. You can do this |
|
127 | Along the way, you should also run the IPython test suite. You can do this | |
128 | using the :command:`iptest` command (which is basically a customized version of |
|
128 | using the :command:`iptest` command (which is basically a customized version of | |
129 | :command:`nosetests`):: |
|
129 | :command:`nosetests`):: | |
130 |
|
130 | |||
131 | $ cd |
|
131 | $ cd | |
132 | $ iptest |
|
132 | $ iptest | |
133 |
|
133 | |||
134 | The :command:`iptest` command will also pick up and run any tests you have |
|
134 | The :command:`iptest` command will also pick up and run any tests you have | |
135 | written. See :ref:`_devel_testing` for further details on the testing system. |
|
135 | written. See :ref:`_devel_testing` for further details on the testing system. | |
136 |
|
136 | |||
137 |
|
137 | |||
138 | Post your branch and request a code review |
|
138 | Post your branch and request a code review | |
139 | ------------------------------------------ |
|
139 | ------------------------------------------ | |
140 |
|
140 | |||
141 | Once you are done with your edits, you should post your branch on Launchpad so |
|
141 | Once you are done with your edits, you should post your branch on Launchpad so | |
142 | that other IPython developers can review the changes and help you merge your |
|
142 | that other IPython developers can review the changes and help you merge your | |
143 | changes into the main development branch. To post your branch on Launchpad, |
|
143 | changes into the main development branch. To post your branch on Launchpad, | |
144 | do:: |
|
144 | do:: | |
145 |
|
145 | |||
146 | $ cd ipython-mybranch |
|
146 | $ cd ipython-mybranch | |
147 | $ bzr push lp:~yourusername/ipython/ipython-mybranch |
|
147 | $ bzr push lp:~yourusername/ipython/ipython-mybranch | |
148 |
|
148 | |||
149 | Then, go to the `IPython Launchpad site <www.launchpad.net/ipython>`_, and you |
|
149 | Then, go to the `IPython Launchpad site <www.launchpad.net/ipython>`_, and you | |
150 | should see your branch under the "Code" tab. If you click on your branch, you |
|
150 | should see your branch under the "Code" tab. If you click on your branch, you | |
151 | can provide a short description of the branch as well as mark its status. Most |
|
151 | can provide a short description of the branch as well as mark its status. Most | |
152 | importantly, you should click the link that reads "Propose for merging into |
|
152 | importantly, you should click the link that reads "Propose for merging into | |
153 | another branch". What does this do? |
|
153 | another branch". What does this do? | |
154 |
|
154 | |||
155 | This let's the other IPython developers know that your branch is ready to be |
|
155 | This let's the other IPython developers know that your branch is ready to be | |
156 | reviewed and merged into the main development branch. During this review |
|
156 | reviewed and merged into the main development branch. During this review | |
157 | process, other developers will give you feedback and help you get your code |
|
157 | process, other developers will give you feedback and help you get your code | |
158 | ready to be merged. What types of things will we be looking for: |
|
158 | ready to be merged. What types of things will we be looking for: | |
159 |
|
159 | |||
160 | * All code is documented. |
|
160 | * All code is documented. | |
161 | * All code has tests. |
|
161 | * All code has tests. | |
162 | * The entire IPython test suite passes. |
|
162 | * The entire IPython test suite passes. | |
163 |
|
163 | |||
164 | Once your changes have been reviewed and approved, someone will merge them |
|
164 | Once your changes have been reviewed and approved, someone will merge them | |
165 | into the main development branch. |
|
165 | into the main development branch. | |
166 |
|
166 | |||
|
167 | ||||
|
168 | Some notes for core developers when merging third-party contributions | |||
|
169 | ===================================================================== | |||
|
170 | ||||
|
171 | Core developers, who ultimately merge any approved branch (from themselves, | |||
|
172 | another developer, or any third-party contribution) will typically use | |||
|
173 | :command:`bzr merge` to merge the branch into the trunk and push it to the main | |||
|
174 | Launcphad site. This is a short list of things to keep in mind when doing this | |||
|
175 | process, so that the project history is easy to understand in the long run, and | |||
|
176 | that generating release notes is as painless and accurate as possible. | |||
|
177 | ||||
|
178 | - When you merge any non-trivial functionality (from one small bug fix to a big | |||
|
179 | feature branch), please remember to always edit the changes_ file | |||
|
180 | accordingly. This file has one main section for each release, and if you | |||
|
181 | edit it as you go, noting what new features, bug fixes or API changes you | |||
|
182 | have made, the release notes will be almost finished when they are needed | |||
|
183 | later. This is much easier if done when you merge the work, rather than | |||
|
184 | weeks or months later by re-reading a massive Bazaar log. | |||
|
185 | ||||
|
186 | - When big merges are done, the practice of putting a summary commit message in | |||
|
187 | the merge is *extremely* useful. It makes this kind of job much nicer, | |||
|
188 | because that summary log message can be almost copy/pasted without changes, | |||
|
189 | if it was well written, rather than dissecting the next-level messages from | |||
|
190 | the individual commits. | |||
|
191 | ||||
|
192 | - It's important that we remember to always credit who gave us something if | |||
|
193 | it's not the committer. In general, we have been fairly good on this front, | |||
|
194 | this is just a reminder to keep things up. As a note, if you are ever | |||
|
195 | committing something that is completely (or almost so) a third-party | |||
|
196 | contribution, do the commit as:: | |||
|
197 | ||||
|
198 | $ bzr commit --author="Someone Else" | |||
|
199 | ||||
|
200 | This way it will show that name separately in the log, which makes it even | |||
|
201 | easier to spot. Obviously we often rework third party contributions | |||
|
202 | extensively, but this is still good to keep in mind for cases when we don't | |||
|
203 | touch the code too much. | |||
|
204 | ||||
|
205 | ||||
167 | Documentation |
|
206 | Documentation | |
168 | ============= |
|
207 | ============= | |
169 |
|
208 | |||
170 | Standalone documentation |
|
209 | Standalone documentation | |
171 | ------------------------ |
|
210 | ------------------------ | |
172 |
|
211 | |||
173 | All standalone documentation should be written in plain text (``.txt``) files |
|
212 | All standalone documentation should be written in plain text (``.txt``) files | |
174 | using reStructuredText [reStructuredText]_ for markup and formatting. All such |
|
213 | using reStructuredText [reStructuredText]_ for markup and formatting. All such | |
175 | documentation should be placed in directory :file:`docs/source` of the IPython |
|
214 | documentation should be placed in directory :file:`docs/source` of the IPython | |
176 | source tree. The documentation in this location will serve as the main source |
|
215 | source tree. The documentation in this location will serve as the main source | |
177 | for IPython documentation and all existing documentation should be converted |
|
216 | for IPython documentation and all existing documentation should be converted | |
178 | to this format. |
|
217 | to this format. | |
179 |
|
218 | |||
180 | To build the final documentation, we use Sphinx [Sphinx]_. Once you have |
|
219 | To build the final documentation, we use Sphinx [Sphinx]_. Once you have | |
181 | Sphinx installed, you can build the html docs yourself by doing:: |
|
220 | Sphinx installed, you can build the html docs yourself by doing:: | |
182 |
|
221 | |||
183 | $ cd ipython-mybranch/docs |
|
222 | $ cd ipython-mybranch/docs | |
184 | $ make html |
|
223 | $ make html | |
185 |
|
224 | |||
186 | Docstring format |
|
225 | Docstring format | |
187 | ---------------- |
|
226 | ---------------- | |
188 |
|
227 | |||
189 | Good docstrings are very important. All new code should have docstrings that |
|
228 | Good docstrings are very important. All new code should have docstrings that | |
190 | are formatted using reStructuredText for markup and formatting, since it is |
|
229 | are formatted using reStructuredText for markup and formatting, since it is | |
191 | understood by a wide variety of tools. Details about using reStructuredText |
|
230 | understood by a wide variety of tools. Details about using reStructuredText | |
192 | for docstrings can be found `here |
|
231 | for docstrings can be found `here | |
193 | <http://epydoc.sourceforge.net/manual-othermarkup.html>`_. |
|
232 | <http://epydoc.sourceforge.net/manual-othermarkup.html>`_. | |
194 |
|
233 | |||
195 | Additional PEPs of interest regarding documentation of code: |
|
234 | Additional PEPs of interest regarding documentation of code: | |
196 |
|
235 | |||
197 | * `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_ |
|
236 | * `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_ | |
198 | * `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_ |
|
237 | * `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_ | |
199 | * `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_ |
|
238 | * `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_ | |
200 |
|
239 | |||
201 |
|
240 | |||
202 | Coding conventions |
|
241 | Coding conventions | |
203 | ================== |
|
242 | ================== | |
204 |
|
243 | |||
205 | General |
|
244 | General | |
206 | ------- |
|
245 | ------- | |
207 |
|
246 | |||
208 | In general, we'll try to follow the standard Python style conventions as |
|
247 | In general, we'll try to follow the standard Python style conventions as | |
209 | described here: |
|
248 | described here: | |
210 |
|
249 | |||
211 | * `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_ |
|
250 | * `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_ | |
212 |
|
251 | |||
213 |
|
252 | |||
214 | Other comments: |
|
253 | Other comments: | |
215 |
|
254 | |||
216 | * In a large file, top level classes and functions should be |
|
255 | * In a large file, top level classes and functions should be | |
217 | separated by 2-3 lines to make it easier to separate them visually. |
|
256 | separated by 2-3 lines to make it easier to separate them visually. | |
218 | * Use 4 spaces for indentation. |
|
257 | * Use 4 spaces for indentation. | |
219 | * Keep the ordering of methods the same in classes that have the same |
|
258 | * Keep the ordering of methods the same in classes that have the same | |
220 | methods. This is particularly true for classes that implement an interface. |
|
259 | methods. This is particularly true for classes that implement an interface. | |
221 |
|
260 | |||
222 | Naming conventions |
|
261 | Naming conventions | |
223 | ------------------ |
|
262 | ------------------ | |
224 |
|
263 | |||
225 | In terms of naming conventions, we'll follow the guidelines from the `Style |
|
264 | In terms of naming conventions, we'll follow the guidelines from the `Style | |
226 | Guide for Python Code`_. |
|
265 | Guide for Python Code`_. | |
227 |
|
266 | |||
228 | For all new IPython code (and much existing code is being refactored), we'll |
|
267 | For all new IPython code (and much existing code is being refactored), we'll | |
229 | use: |
|
268 | use: | |
230 |
|
269 | |||
231 | * All ``lowercase`` module names. |
|
270 | * All ``lowercase`` module names. | |
232 |
|
271 | |||
233 | * ``CamelCase`` for class names. |
|
272 | * ``CamelCase`` for class names. | |
234 |
|
273 | |||
235 | * ``lowercase_with_underscores`` for methods, functions, variables and |
|
274 | * ``lowercase_with_underscores`` for methods, functions, variables and | |
236 | attributes. |
|
275 | attributes. | |
237 |
|
276 | |||
238 | There are, however, some important exceptions to these rules. In some cases, |
|
277 | There are, however, some important exceptions to these rules. In some cases, | |
239 | IPython code will interface with packages (Twisted, Wx, Qt) that use other |
|
278 | IPython code will interface with packages (Twisted, Wx, Qt) that use other | |
240 | conventions. At some level this makes it impossible to adhere to our own |
|
279 | conventions. At some level this makes it impossible to adhere to our own | |
241 | standards at all times. In particular, when subclassing classes that use other |
|
280 | standards at all times. In particular, when subclassing classes that use other | |
242 | naming conventions, you must follow their naming conventions. To deal with |
|
281 | naming conventions, you must follow their naming conventions. To deal with | |
243 | cases like this, we propose the following policy: |
|
282 | cases like this, we propose the following policy: | |
244 |
|
283 | |||
245 | * If you are subclassing a class that uses different conventions, use its |
|
284 | * If you are subclassing a class that uses different conventions, use its | |
246 | naming conventions throughout your subclass. Thus, if you are creating a |
|
285 | naming conventions throughout your subclass. Thus, if you are creating a | |
247 | Twisted Protocol class, used Twisted's |
|
286 | Twisted Protocol class, used Twisted's | |
248 | ``namingSchemeForMethodsAndAttributes.`` |
|
287 | ``namingSchemeForMethodsAndAttributes.`` | |
249 |
|
288 | |||
250 | * All IPython's official interfaces should use our conventions. In some cases |
|
289 | * All IPython's official interfaces should use our conventions. In some cases | |
251 | this will mean that you need to provide shadow names (first implement |
|
290 | this will mean that you need to provide shadow names (first implement | |
252 | ``fooBar`` and then ``foo_bar = fooBar``). We want to avoid this at all |
|
291 | ``fooBar`` and then ``foo_bar = fooBar``). We want to avoid this at all | |
253 | costs, but it will probably be necessary at times. But, please use this |
|
292 | costs, but it will probably be necessary at times. But, please use this | |
254 | sparingly! |
|
293 | sparingly! | |
255 |
|
294 | |||
256 | Implementation-specific *private* methods will use |
|
295 | Implementation-specific *private* methods will use | |
257 | ``_single_underscore_prefix``. Names with a leading double underscore will |
|
296 | ``_single_underscore_prefix``. Names with a leading double underscore will | |
258 | *only* be used in special cases, as they makes subclassing difficult (such |
|
297 | *only* be used in special cases, as they makes subclassing difficult (such | |
259 | names are not easily seen by child classes). |
|
298 | names are not easily seen by child classes). | |
260 |
|
299 | |||
261 | Occasionally some run-in lowercase names are used, but mostly for very short |
|
300 | Occasionally some run-in lowercase names are used, but mostly for very short | |
262 | names or where we are implementing methods very similar to existing ones in a |
|
301 | names or where we are implementing methods very similar to existing ones in a | |
263 | base class (like ``runlines()`` where ``runsource()`` and ``runcode()`` had |
|
302 | base class (like ``runlines()`` where ``runsource()`` and ``runcode()`` had | |
264 | established precedent). |
|
303 | established precedent). | |
265 |
|
304 | |||
266 | The old IPython codebase has a big mix of classes and modules prefixed with an |
|
305 | The old IPython codebase has a big mix of classes and modules prefixed with an | |
267 | explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned |
|
306 | explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned | |
268 | upon, as namespaces offer cleaner prefixing. The only case where this approach |
|
307 | upon, as namespaces offer cleaner prefixing. The only case where this approach | |
269 | is justified is for classes which are expected to be imported into external |
|
308 | is justified is for classes which are expected to be imported into external | |
270 | namespaces and a very generic name (like Shell) is too likely to clash with |
|
309 | namespaces and a very generic name (like Shell) is too likely to clash with | |
271 | something else. We'll need to revisit this issue as we clean up and refactor |
|
310 | something else. We'll need to revisit this issue as we clean up and refactor | |
272 | the code, but in general we should remove as many unnecessary ``IP``/``ip`` |
|
311 | the code, but in general we should remove as many unnecessary ``IP``/``ip`` | |
273 | prefixes as possible. However, if a prefix seems absolutely necessary the more |
|
312 | prefixes as possible. However, if a prefix seems absolutely necessary the more | |
274 | specific ``IPY`` or ``ipy`` are preferred. |
|
313 | specific ``IPY`` or ``ipy`` are preferred. | |
275 |
|
314 | |||
276 | .. _devel_testing: |
|
315 | .. _devel_testing: | |
277 |
|
316 | |||
278 | Testing system |
|
317 | Testing system | |
279 | ============== |
|
318 | ============== | |
280 |
|
319 | |||
281 | It is extremely important that all code contributed to IPython has tests. |
|
320 | It is extremely important that all code contributed to IPython has tests. | |
282 | Tests should be written as unittests, doctests or as entities that the Nose |
|
321 | Tests should be written as unittests, doctests or as entities that the Nose | |
283 | [Nose]_ testing package will find. Regardless of how the tests are written, we |
|
322 | [Nose]_ testing package will find. Regardless of how the tests are written, we | |
284 | will use Nose for discovering and running the tests. Nose will be required to |
|
323 | will use Nose for discovering and running the tests. Nose will be required to | |
285 | run the IPython test suite, but will not be required to simply use IPython. |
|
324 | run the IPython test suite, but will not be required to simply use IPython. | |
286 |
|
325 | |||
287 | Tests of Twisted using code need to follow two additional guidelines: |
|
326 | Tests of Twisted using code need to follow two additional guidelines: | |
288 |
|
327 | |||
289 | 1. Twisted using tests should be written by subclassing the :class:`TestCase` |
|
328 | 1. Twisted using tests should be written by subclassing the :class:`TestCase` | |
290 | class that comes with :mod:`twisted.trial.unittest`. |
|
329 | class that comes with :mod:`twisted.trial.unittest`. | |
291 |
|
330 | |||
292 | 2. All :class:`Deferred` instances that are created in the test must be |
|
331 | 2. All :class:`Deferred` instances that are created in the test must be | |
293 | properly chained and the final one *must* be the return value of the test |
|
332 | properly chained and the final one *must* be the return value of the test | |
294 | method. |
|
333 | method. | |
295 |
|
334 | |||
296 | When these two things are done, Nose will be able to run the tests and the |
|
335 | When these two things are done, Nose will be able to run the tests and the | |
297 | twisted reactor will be handled correctly. |
|
336 | twisted reactor will be handled correctly. | |
298 |
|
337 | |||
299 | Each subpackage in IPython should have its own :file:`tests` directory that |
|
338 | Each subpackage in IPython should have its own :file:`tests` directory that | |
300 | contains all of the tests for that subpackage. This allows each subpackage to |
|
339 | contains all of the tests for that subpackage. This allows each subpackage to | |
301 | be self-contained. A good convention to follow is to have a file named |
|
340 | be self-contained. A good convention to follow is to have a file named | |
302 | :file:`test_foo.py` for each module :file:`foo.py` in the package. This makes |
|
341 | :file:`test_foo.py` for each module :file:`foo.py` in the package. This makes | |
303 | it easy to organize the tests, though like most conventions, it's OK to break |
|
342 | it easy to organize the tests, though like most conventions, it's OK to break | |
304 | it if logic and common sense dictate otherwise. |
|
343 | it if logic and common sense dictate otherwise. | |
305 |
|
344 | |||
306 | If a subpackage has any dependencies beyond the Python standard library, the |
|
345 | If a subpackage has any dependencies beyond the Python standard library, the | |
307 | tests for that subpackage should be skipped if the dependencies are not |
|
346 | tests for that subpackage should be skipped if the dependencies are not | |
308 | found. This is very important so users don't get tests failing simply because |
|
347 | found. This is very important so users don't get tests failing simply because | |
309 | they don't have dependencies. We ship a set of decorators in the |
|
348 | they don't have dependencies. We ship a set of decorators in the | |
310 | :mod:`IPython.testing` package to tag tests that may be platform-specific or |
|
349 | :mod:`IPython.testing` package to tag tests that may be platform-specific or | |
311 | otherwise may have restrictions; if the existing ones don't fit your needs, add |
|
350 | otherwise may have restrictions; if the existing ones don't fit your needs, add | |
312 | a new decorator in that location so other tests can reuse it. |
|
351 | a new decorator in that location so other tests can reuse it. | |
313 |
|
352 | |||
314 | To run the IPython test suite, use the :command:`iptest` command that is |
|
353 | To run the IPython test suite, use the :command:`iptest` command that is | |
315 | installed with IPython (if you are using IPython in-place, without installing |
|
354 | installed with IPython (if you are using IPython in-place, without installing | |
316 | it, you can find this script in the :file:`scripts` directory):: |
|
355 | it, you can find this script in the :file:`scripts` directory):: | |
317 |
|
356 | |||
318 | $ iptest |
|
357 | $ iptest | |
319 |
|
358 | |||
320 | This command runs Nose with the proper options and extensions. By default, |
|
359 | This command colects all IPython tests into separate groups, and then calls | |
321 | :command:`iptest` runs the entire IPython test suite (skipping tests that may |
|
360 | either Nose with the proper options and extensions, or Twisted's | |
322 | be platform-specific or which depend on tools you may not have). But you can |
|
361 | :command:`trial`. This ensures that tests that need the Twisted reactor | |
323 | also use it to run only one specific test file, or a specific test function. |
|
362 | management facilities execute separate of Nose. If any individual test group | |
324 | For example, this will run only the :file:`test_magic` file from the test |
|
363 | fails, :command:`iptest` will print what you need to type so you can rerun that | |
325 | suite:: |
|
364 | particular test group alone for debugging. | |
|
365 | ||||
|
366 | By default, :command:`iptest` runs the entire IPython test | |||
|
367 | suite (skipping tests that may be platform-specific or which depend on tools | |||
|
368 | you may not have). But you can also use it to run only one specific test file, | |||
|
369 | or a specific test function. For example, this will run only the | |||
|
370 | :file:`test_magic` file from the test suite:: | |||
326 |
|
371 | |||
327 | $ iptest IPython.tests.test_magic |
|
372 | $ iptest IPython.tests.test_magic | |
328 | ---------------------------------------------------------------------- |
|
373 | ---------------------------------------------------------------------- | |
329 | Ran 10 tests in 0.348s |
|
374 | Ran 10 tests in 0.348s | |
330 |
|
375 | |||
331 | OK (SKIP=3) |
|
376 | OK (SKIP=3) | |
332 | Deleting object: second_pass |
|
377 | Deleting object: second_pass | |
333 |
|
378 | |||
334 | while the ``path:function`` syntax allows you to select a specific function in |
|
379 | while the ``path:function`` syntax allows you to select a specific function in | |
335 | that file to run:: |
|
380 | that file to run:: | |
336 |
|
381 | |||
337 | $ iptest IPython.tests.test_magic:test_obj_del |
|
382 | $ iptest IPython.tests.test_magic:test_obj_del | |
338 | ---------------------------------------------------------------------- |
|
383 | ---------------------------------------------------------------------- | |
339 | Ran 1 test in 0.204s |
|
384 | Ran 1 test in 0.204s | |
340 |
|
385 | |||
341 | OK |
|
386 | OK | |
342 |
|
387 | |||
343 | Since :command:`iptest` is based on nosetests, you can pass it any regular |
|
388 | Since :command:`iptest` is based on nosetests, you can pass it any regular | |
344 | nosetests option. For example, you can use ``--pdb`` or ``--pdb-failures`` to |
|
389 | nosetests option. For example, you can use ``--pdb`` or ``--pdb-failures`` to | |
345 | automatically activate the interactive Pdb debugger on errors or failures. See |
|
390 | automatically activate the interactive Pdb debugger on errors or failures. See | |
346 | the nosetests documentation for further details. |
|
391 | the nosetests documentation for further details. | |
347 |
|
392 | |||
348 | .. warning:: |
|
|||
349 |
|
||||
350 | Note that right now we have a nasty interaction between ipdoctest and |
|
|||
351 | twisted. Until we figure this out, please use the following instructions to |
|
|||
352 | ensure that at least you run all the tests. |
|
|||
353 |
|
||||
354 | Right now, if you now run:: |
|
|||
355 |
|
||||
356 | $ iptest [any options] [any submodules] |
|
|||
357 |
|
||||
358 | it will NOT load ipdoctest but won't cause any Twisted problems. |
|
|||
359 |
|
||||
360 | Once you're happy that you didn't break Twisted, run:: |
|
|||
361 |
|
||||
362 | $ iptest --with-ipdoctest [any options] [any submodules] |
|
|||
363 |
|
||||
364 | This MAY give a Twisted AlreadyCalledError exception at the end, but it will |
|
|||
365 | also correctly load up all of the ipython-specific tests and doctests. |
|
|||
366 |
|
||||
367 | The above can be made easier with a trivial shell alias:: |
|
|||
368 |
|
||||
369 | $ alias iptest2='iptest --with-ipdoctest' |
|
|||
370 |
|
||||
371 | So that you can run:: |
|
|||
372 |
|
||||
373 | $ iptest ... |
|
|||
374 | # Twisted happy |
|
|||
375 | # iptest2 ... |
|
|||
376 | # ignore possible Twisted error, this checks all the rest. |
|
|||
377 |
|
||||
378 |
|
393 | |||
379 | A few tips for writing tests |
|
394 | A few tips for writing tests | |
380 | ---------------------------- |
|
395 | ---------------------------- | |
381 |
|
396 | |||
382 | You can write tests either as normal test files, using all the conventions that |
|
397 | You can write tests either as normal test files, using all the conventions that | |
383 | Nose recognizes, or as doctests. Note that *all* IPython functions should have |
|
398 | Nose recognizes, or as doctests. Note that *all* IPython functions should have | |
384 | at least one example that serves as a doctest, whenever technically feasible. |
|
399 | at least one example that serves as a doctest, whenever technically feasible. | |
385 | However, example doctests should only be in the main docstring if they are *a |
|
400 | However, example doctests should only be in the main docstring if they are *a | |
386 | good example*, i.e. if they convey useful information about the function. If |
|
401 | good example*, i.e. if they convey useful information about the function. If | |
387 | you simply would like to write a test as a doctest, put it in a separate test |
|
402 | you simply would like to write a test as a doctest, put it in a separate test | |
388 | file and write a no-op function whose only purpose is its docstring. |
|
403 | file and write a no-op function whose only purpose is its docstring. | |
389 |
|
404 | |||
390 | Note, however, that in a file named :file:`test_X`, functions whose only test |
|
405 | Note, however, that in a file named :file:`test_X`, functions whose only test | |
391 | is their docstring (as a doctest) and which have no test functionality of their |
|
406 | is their docstring (as a doctest) and which have no test functionality of their | |
392 | own, should be called *doctest_foo* instead of *test_foo*, otherwise they get |
|
407 | own, should be called *doctest_foo* instead of *test_foo*, otherwise they get | |
393 | double-counted (the empty function call is counted as a test, which just |
|
408 | double-counted (the empty function call is counted as a test, which just | |
394 | inflates tests numbers artificially). This restriction does not apply to |
|
409 | inflates tests numbers artificially). This restriction does not apply to | |
395 | functions in files with other names, due to how Nose discovers tests. |
|
410 | functions in files with other names, due to how Nose discovers tests. | |
396 |
|
411 | |||
397 | You can use IPython examples in your docstrings. Those can make full use of |
|
412 | You can use IPython examples in your docstrings. Those can make full use of | |
398 | IPython functionality (magics, variable substitution, etc), but be careful to |
|
413 | IPython functionality (magics, variable substitution, etc), but be careful to | |
399 | keep them generic enough that they run identically on all Operating Systems. |
|
414 | keep them generic enough that they run identically on all Operating Systems. | |
400 |
|
415 | |||
401 | The prompts in your doctests can be either of the plain Python ``>>>`` variety |
|
416 | The prompts in your doctests can be either of the plain Python ``>>>`` variety | |
402 | or ``In [1]:`` IPython style. Since this is the IPython system, after all, we |
|
417 | or ``In [1]:`` IPython style. Since this is the IPython system, after all, we | |
403 | encourage you to use IPython prompts throughout, unless you are illustrating a |
|
418 | encourage you to use IPython prompts throughout, unless you are illustrating a | |
404 | specific aspect of the normal prompts (such as the ``%doctest_mode`` magic). |
|
419 | specific aspect of the normal prompts (such as the ``%doctest_mode`` magic). | |
405 |
|
420 | |||
406 | If a test isn't safe to run inside the main nose process (e.g. because it loads |
|
421 | If a test isn't safe to run inside the main nose process (e.g. because it loads | |
407 | a GUI toolkit), consider running it in a subprocess and capturing its output |
|
422 | a GUI toolkit), consider running it in a subprocess and capturing its output | |
408 | for evaluation and test decision later. Here is an example of how to do it, by |
|
423 | for evaluation and test decision later. Here is an example of how to do it, by | |
409 | relying on the builtin ``_ip`` object that contains the public IPython api as |
|
424 | relying on the builtin ``_ip`` object that contains the public IPython api as | |
410 | defined in :mod:`IPython.ipapi`:: |
|
425 | defined in :mod:`IPython.ipapi`:: | |
411 |
|
426 | |||
412 | def test_obj_del(): |
|
427 | def test_obj_del(): | |
413 | """Test that object's __del__ methods are called on exit.""" |
|
428 | """Test that object's __del__ methods are called on exit.""" | |
414 | test_dir = os.path.dirname(__file__) |
|
429 | test_dir = os.path.dirname(__file__) | |
415 | del_file = os.path.join(test_dir,'obj_del.py') |
|
430 | del_file = os.path.join(test_dir,'obj_del.py') | |
416 | out = _ip.IP.getoutput('ipython %s' % del_file) |
|
431 | out = _ip.IP.getoutput('ipython %s' % del_file) | |
417 | nt.assert_equals(out,'object A deleted') |
|
432 | nt.assert_equals(out,'object A deleted') | |
418 |
|
433 | |||
419 |
|
434 | |||
420 |
|
435 | |||
421 | If a doctest contains input whose output you don't want to verify identically |
|
436 | If a doctest contains input whose output you don't want to verify identically | |
422 | via doctest (random output, an object id, etc), you can mark a docstring with |
|
437 | via doctest (random output, an object id, etc), you can mark a docstring with | |
423 | ``#random``. All of these test will have their code executed but no output |
|
438 | ``#random``. All of these test will have their code executed but no output | |
424 | checking will be done:: |
|
439 | checking will be done:: | |
425 |
|
440 | |||
426 | >>> 1+3 |
|
441 | >>> 1+3 | |
427 | junk goes here... # random |
|
442 | junk goes here... # random | |
428 |
|
443 | |||
429 | >>> 1+2 |
|
444 | >>> 1+2 | |
430 | again, anything goes #random |
|
445 | again, anything goes #random | |
431 | if multiline, the random mark is only needed once. |
|
446 | if multiline, the random mark is only needed once. | |
432 |
|
447 | |||
433 | >>> 1+2 |
|
448 | >>> 1+2 | |
434 | You can also put the random marker at the end: |
|
449 | You can also put the random marker at the end: | |
435 | # random |
|
450 | # random | |
436 |
|
451 | |||
437 | >>> 1+2 |
|
452 | >>> 1+2 | |
438 | # random |
|
453 | # random | |
439 | .. or at the beginning. |
|
454 | .. or at the beginning. | |
440 |
|
455 | |||
441 | In a case where you want an *entire* docstring to be executed but not verified |
|
456 | In a case where you want an *entire* docstring to be executed but not verified | |
442 | (this only serves to check that the code runs without crashing, so it should be |
|
457 | (this only serves to check that the code runs without crashing, so it should be | |
443 | used very sparingly), you can put ``# all-random`` in the docstring. |
|
458 | used very sparingly), you can put ``# all-random`` in the docstring. | |
444 |
|
459 | |||
445 | .. _devel_config: |
|
460 | .. _devel_config: | |
446 |
|
461 | |||
447 | Release checklist |
|
462 | Release checklist | |
448 | ================= |
|
463 | ================= | |
449 |
|
464 | |||
450 | Most of the release process is automated by the :file:`release` script in the |
|
465 | Most of the release process is automated by the :file:`release` script in the | |
451 | :file:`tools` directory. This is just a handy reminder for the release manager. |
|
466 | :file:`tools` directory. This is just a handy reminder for the release manager. | |
452 |
|
467 | |||
|
468 | #. First, run :file:`build_release`, which does all the file checking and | |||
|
469 | building that the real release script will do. This will let you do test | |||
|
470 | installations, check that the build procedure runs OK, etc. You may want to | |||
|
471 | disable a few things like multi-version RPM building while testing, because | |||
|
472 | otherwise the build takes really long. | |||
|
473 | ||||
453 | #. Run the release script, which makes the tar.gz, eggs and Win32 .exe |
|
474 | #. Run the release script, which makes the tar.gz, eggs and Win32 .exe | |
454 | installer. It posts them to the site and registers the release with PyPI. |
|
475 | installer. It posts them to the site and registers the release with PyPI. | |
455 |
|
476 | |||
456 | #. Updating the website with announcements and links to the updated |
|
477 | #. Updating the website with announcements and links to the updated | |
457 | changes.txt in html form. Remember to put a short note both on the news |
|
478 | changes.txt in html form. Remember to put a short note both on the news | |
458 | page of the site and on Launcphad. |
|
479 | page of the site and on Launcphad. | |
459 |
|
480 | |||
460 | #. Drafting a short release announcement with i) highlights and ii) a link to |
|
481 | #. Drafting a short release announcement with i) highlights and ii) a link to | |
461 | the html changes.txt. |
|
482 | the html changes.txt. | |
462 |
|
483 | |||
463 | #. Make sure that the released version of the docs is live on the site. |
|
484 | #. Make sure that the released version of the docs is live on the site. | |
464 |
|
485 | |||
465 | #. Celebrate! |
|
486 | #. Celebrate! | |
466 |
|
487 | |||
467 | Porting to 3.0 |
|
488 | Porting to 3.0 | |
468 | ============== |
|
489 | ============== | |
469 |
|
490 | |||
470 | There are no definite plans for porting of IPython to python 3. The major |
|
491 | There are no definite plans for porting of IPython to python 3. The major | |
471 | issue is the dependency on twisted framework for the networking/threading |
|
492 | issue is the dependency on twisted framework for the networking/threading | |
472 | stuff. It is possible that it the traditional IPython interactive console |
|
493 | stuff. It is possible that it the traditional IPython interactive console | |
473 | could be ported more easily since it has no such dependency. Here are a few |
|
494 | could be ported more easily since it has no such dependency. Here are a few | |
474 | things that will need to be considered when doing such a port especially |
|
495 | things that will need to be considered when doing such a port especially | |
475 | if we want to have a codebase that works directly on both 2.x and 3.x. |
|
496 | if we want to have a codebase that works directly on both 2.x and 3.x. | |
476 |
|
497 | |||
477 | 1. The syntax for exceptions changed (PEP 3110). The old |
|
498 | 1. The syntax for exceptions changed (PEP 3110). The old | |
478 | `except exc, var` changed to `except exc as var`. At last |
|
499 | `except exc, var` changed to `except exc as var`. At last | |
479 | count there was 78 occurences of this usage in the codebase. This |
|
500 | count there was 78 occurences of this usage in the codebase. This | |
480 | is a particularly problematic issue, because it's not easy to |
|
501 | is a particularly problematic issue, because it's not easy to | |
481 | implement it in a 2.5-compatible way. |
|
502 | implement it in a 2.5-compatible way. | |
482 |
|
503 | |||
483 | Because it is quite difficult to support simultaneously Python 2.5 and 3.x, we |
|
504 | Because it is quite difficult to support simultaneously Python 2.5 and 3.x, we | |
484 | will likely at some point put out a release that requires strictly 2.6 and |
|
505 | will likely at some point put out a release that requires strictly 2.6 and | |
485 | abandons 2.5 compatibility. This will then allow us to port the code to using |
|
506 | abandons 2.5 compatibility. This will then allow us to port the code to using | |
486 | :func:`print` as a function, `except exc as var` syntax, etc. But as of |
|
507 | :func:`print` as a function, `except exc as var` syntax, etc. But as of | |
487 | version 0.11 at least, we will retain Python 2.5 compatibility. |
|
508 | version 0.11 at least, we will retain Python 2.5 compatibility. | |
488 |
|
509 | |||
489 |
|
510 | |||
490 | .. [Bazaar] Bazaar. http://bazaar-vcs.org/ |
|
511 | .. [Bazaar] Bazaar. http://bazaar-vcs.org/ | |
491 | .. [Launchpad] Launchpad. http://www.launchpad.net/ipython |
|
512 | .. [Launchpad] Launchpad. http://www.launchpad.net/ipython | |
492 | .. [reStructuredText] reStructuredText. http://docutils.sourceforge.net/rst.html |
|
513 | .. [reStructuredText] reStructuredText. http://docutils.sourceforge.net/rst.html | |
493 | .. [Sphinx] Sphinx. http://sphinx.pocoo.org/ |
|
514 | .. [Sphinx] Sphinx. http://sphinx.pocoo.org/ | |
494 | .. [Nose] Nose: a discovery based unittest extension. http://code.google.com/p/python-nose/ |
|
515 | .. [Nose] Nose: a discovery based unittest extension. http://code.google.com/p/python-nose/ |
General Comments 0
You need to be logged in to leave comments.
Login now