##// END OF EJS Templates
Fix bug tracker address to point to github
Fernando Perez -
Show More
@@ -1,665 +1,664 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The :class:`~IPython.core.application.Application` object for the command
4 The :class:`~IPython.core.application.Application` object for the command
5 line :command:`ipython` program.
5 line :command:`ipython` program.
6
6
7 Authors
7 Authors
8 -------
8 -------
9
9
10 * Brian Granger
10 * Brian Granger
11 * Fernando Perez
11 * Fernando Perez
12 """
12 """
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2008-2010 The IPython Development Team
15 # Copyright (C) 2008-2010 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 from __future__ import absolute_import
25 from __future__ import absolute_import
26
26
27 import logging
27 import logging
28 import os
28 import os
29 import sys
29 import sys
30
30
31 from IPython.core import release
31 from IPython.core import release
32 from IPython.core.crashhandler import CrashHandler
32 from IPython.core.crashhandler import CrashHandler
33 from IPython.core.application import Application, BaseAppConfigLoader
33 from IPython.core.application import Application, BaseAppConfigLoader
34 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
34 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
35 from IPython.config.loader import (
35 from IPython.config.loader import (
36 Config,
36 Config,
37 PyFileConfigLoader
37 PyFileConfigLoader
38 )
38 )
39 from IPython.lib import inputhook
39 from IPython.lib import inputhook
40 from IPython.utils.path import filefind, get_ipython_dir
40 from IPython.utils.path import filefind, get_ipython_dir
41 from IPython.core import usage
41 from IPython.core import usage
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Globals, utilities and helpers
44 # Globals, utilities and helpers
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 #: The default config file name for this application.
47 #: The default config file name for this application.
48 default_config_file_name = u'ipython_config.py'
48 default_config_file_name = u'ipython_config.py'
49
49
50
50
51 class IPAppConfigLoader(BaseAppConfigLoader):
51 class IPAppConfigLoader(BaseAppConfigLoader):
52
52
53 def _add_arguments(self):
53 def _add_arguments(self):
54 super(IPAppConfigLoader, self)._add_arguments()
54 super(IPAppConfigLoader, self)._add_arguments()
55 paa = self.parser.add_argument
55 paa = self.parser.add_argument
56 paa('-p',
56 paa('-p',
57 '--profile', dest='Global.profile', type=unicode,
57 '--profile', dest='Global.profile', type=unicode,
58 help=
58 help=
59 """The string name of the ipython profile to be used. Assume that your
59 """The string name of the ipython profile to be used. Assume that your
60 config file is ipython_config-<name>.py (looks in current dir first,
60 config file is ipython_config-<name>.py (looks in current dir first,
61 then in IPYTHON_DIR). This is a quick way to keep and load multiple
61 then in IPYTHON_DIR). This is a quick way to keep and load multiple
62 config files for different tasks, especially if include your basic one
62 config files for different tasks, especially if include your basic one
63 in your more specialized ones. You can keep a basic
63 in your more specialized ones. You can keep a basic
64 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
64 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
65 include this one and load extra things for particular tasks.""",
65 include this one and load extra things for particular tasks.""",
66 metavar='Global.profile')
66 metavar='Global.profile')
67 paa('--config-file',
67 paa('--config-file',
68 dest='Global.config_file', type=unicode,
68 dest='Global.config_file', type=unicode,
69 help=
69 help=
70 """Set the config file name to override default. Normally IPython
70 """Set the config file name to override default. Normally IPython
71 loads ipython_config.py (from current directory) or
71 loads ipython_config.py (from current directory) or
72 IPYTHON_DIR/ipython_config.py. If the loading of your config file
72 IPYTHON_DIR/ipython_config.py. If the loading of your config file
73 fails, IPython starts with a bare bones configuration (no modules
73 fails, IPython starts with a bare bones configuration (no modules
74 loaded at all).""",
74 loaded at all).""",
75 metavar='Global.config_file')
75 metavar='Global.config_file')
76 paa('--autocall',
76 paa('--autocall',
77 dest='InteractiveShell.autocall', type=int,
77 dest='InteractiveShell.autocall', type=int,
78 help=
78 help=
79 """Make IPython automatically call any callable object even if you
79 """Make IPython automatically call any callable object even if you
80 didn't type explicit parentheses. For example, 'str 43' becomes
80 didn't type explicit parentheses. For example, 'str 43' becomes
81 'str(43)' automatically. The value can be '0' to disable the feature,
81 'str(43)' automatically. The value can be '0' to disable the feature,
82 '1' for 'smart' autocall, where it is not applied if there are no more
82 '1' for 'smart' autocall, where it is not applied if there are no more
83 arguments on the line, and '2' for 'full' autocall, where all callable
83 arguments on the line, and '2' for 'full' autocall, where all callable
84 objects are automatically called (even if no arguments are present).
84 objects are automatically called (even if no arguments are present).
85 The default is '1'.""",
85 The default is '1'.""",
86 metavar='InteractiveShell.autocall')
86 metavar='InteractiveShell.autocall')
87 paa('--autoindent',
87 paa('--autoindent',
88 action='store_true', dest='InteractiveShell.autoindent',
88 action='store_true', dest='InteractiveShell.autoindent',
89 help='Turn on autoindenting.')
89 help='Turn on autoindenting.')
90 paa('--no-autoindent',
90 paa('--no-autoindent',
91 action='store_false', dest='InteractiveShell.autoindent',
91 action='store_false', dest='InteractiveShell.autoindent',
92 help='Turn off autoindenting.')
92 help='Turn off autoindenting.')
93 paa('--automagic',
93 paa('--automagic',
94 action='store_true', dest='InteractiveShell.automagic',
94 action='store_true', dest='InteractiveShell.automagic',
95 help=
95 help=
96 """Turn on the auto calling of magic commands. Type %%magic at the
96 """Turn on the auto calling of magic commands. Type %%magic at the
97 IPython prompt for more information.""")
97 IPython prompt for more information.""")
98 paa('--no-automagic',
98 paa('--no-automagic',
99 action='store_false', dest='InteractiveShell.automagic',
99 action='store_false', dest='InteractiveShell.automagic',
100 help='Turn off the auto calling of magic commands.')
100 help='Turn off the auto calling of magic commands.')
101 paa('--autoedit-syntax',
101 paa('--autoedit-syntax',
102 action='store_true', dest='TerminalInteractiveShell.autoedit_syntax',
102 action='store_true', dest='TerminalInteractiveShell.autoedit_syntax',
103 help='Turn on auto editing of files with syntax errors.')
103 help='Turn on auto editing of files with syntax errors.')
104 paa('--no-autoedit-syntax',
104 paa('--no-autoedit-syntax',
105 action='store_false', dest='TerminalInteractiveShell.autoedit_syntax',
105 action='store_false', dest='TerminalInteractiveShell.autoedit_syntax',
106 help='Turn off auto editing of files with syntax errors.')
106 help='Turn off auto editing of files with syntax errors.')
107 paa('--banner',
107 paa('--banner',
108 action='store_true', dest='Global.display_banner',
108 action='store_true', dest='Global.display_banner',
109 help='Display a banner upon starting IPython.')
109 help='Display a banner upon starting IPython.')
110 paa('--no-banner',
110 paa('--no-banner',
111 action='store_false', dest='Global.display_banner',
111 action='store_false', dest='Global.display_banner',
112 help="Don't display a banner upon starting IPython.")
112 help="Don't display a banner upon starting IPython.")
113 paa('--cache-size',
113 paa('--cache-size',
114 type=int, dest='InteractiveShell.cache_size',
114 type=int, dest='InteractiveShell.cache_size',
115 help=
115 help=
116 """Set the size of the output cache. The default is 1000, you can
116 """Set the size of the output cache. The default is 1000, you can
117 change it permanently in your config file. Setting it to 0 completely
117 change it permanently in your config file. Setting it to 0 completely
118 disables the caching system, and the minimum value accepted is 20 (if
118 disables the caching system, and the minimum value accepted is 20 (if
119 you provide a value less than 20, it is reset to 0 and a warning is
119 you provide a value less than 20, it is reset to 0 and a warning is
120 issued). This limit is defined because otherwise you'll spend more
120 issued). This limit is defined because otherwise you'll spend more
121 time re-flushing a too small cache than working""",
121 time re-flushing a too small cache than working""",
122 metavar='InteractiveShell.cache_size')
122 metavar='InteractiveShell.cache_size')
123 paa('--classic',
123 paa('--classic',
124 action='store_true', dest='Global.classic',
124 action='store_true', dest='Global.classic',
125 help="Gives IPython a similar feel to the classic Python prompt.")
125 help="Gives IPython a similar feel to the classic Python prompt.")
126 paa('--colors',
126 paa('--colors',
127 type=str, dest='InteractiveShell.colors',
127 type=str, dest='InteractiveShell.colors',
128 help="Set the color scheme (NoColor, Linux, and LightBG).",
128 help="Set the color scheme (NoColor, Linux, and LightBG).",
129 metavar='InteractiveShell.colors')
129 metavar='InteractiveShell.colors')
130 paa('--color-info',
130 paa('--color-info',
131 action='store_true', dest='InteractiveShell.color_info',
131 action='store_true', dest='InteractiveShell.color_info',
132 help=
132 help=
133 """IPython can display information about objects via a set of func-
133 """IPython can display information about objects via a set of func-
134 tions, and optionally can use colors for this, syntax highlighting
134 tions, and optionally can use colors for this, syntax highlighting
135 source code and various other elements. However, because this
135 source code and various other elements. However, because this
136 information is passed through a pager (like 'less') and many pagers get
136 information is passed through a pager (like 'less') and many pagers get
137 confused with color codes, this option is off by default. You can test
137 confused with color codes, this option is off by default. You can test
138 it and turn it on permanently in your ipython_config.py file if it
138 it and turn it on permanently in your ipython_config.py file if it
139 works for you. Test it and turn it on permanently if it works with
139 works for you. Test it and turn it on permanently if it works with
140 your system. The magic function %%color_info allows you to toggle this
140 your system. The magic function %%color_info allows you to toggle this
141 inter- actively for testing.""")
141 inter- actively for testing.""")
142 paa('--no-color-info',
142 paa('--no-color-info',
143 action='store_false', dest='InteractiveShell.color_info',
143 action='store_false', dest='InteractiveShell.color_info',
144 help="Disable using colors for info related things.")
144 help="Disable using colors for info related things.")
145 paa('--confirm-exit',
145 paa('--confirm-exit',
146 action='store_true', dest='TerminalInteractiveShell.confirm_exit',
146 action='store_true', dest='TerminalInteractiveShell.confirm_exit',
147 help=
147 help=
148 """Set to confirm when you try to exit IPython with an EOF (Control-D
148 """Set to confirm when you try to exit IPython with an EOF (Control-D
149 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
149 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
150 '%%Exit', you can force a direct exit without any confirmation.""")
150 '%%Exit', you can force a direct exit without any confirmation.""")
151 paa('--no-confirm-exit',
151 paa('--no-confirm-exit',
152 action='store_false', dest='TerminalInteractiveShell.confirm_exit',
152 action='store_false', dest='TerminalInteractiveShell.confirm_exit',
153 help="Don't prompt the user when exiting.")
153 help="Don't prompt the user when exiting.")
154 paa('--deep-reload',
154 paa('--deep-reload',
155 action='store_true', dest='InteractiveShell.deep_reload',
155 action='store_true', dest='InteractiveShell.deep_reload',
156 help=
156 help=
157 """Enable deep (recursive) reloading by default. IPython can use the
157 """Enable deep (recursive) reloading by default. IPython can use the
158 deep_reload module which reloads changes in modules recursively (it
158 deep_reload module which reloads changes in modules recursively (it
159 replaces the reload() function, so you don't need to change anything to
159 replaces the reload() function, so you don't need to change anything to
160 use it). deep_reload() forces a full reload of modules whose code may
160 use it). deep_reload() forces a full reload of modules whose code may
161 have changed, which the default reload() function does not. When
161 have changed, which the default reload() function does not. When
162 deep_reload is off, IPython will use the normal reload(), but
162 deep_reload is off, IPython will use the normal reload(), but
163 deep_reload will still be available as dreload(). This fea- ture is off
163 deep_reload will still be available as dreload(). This fea- ture is off
164 by default [which means that you have both normal reload() and
164 by default [which means that you have both normal reload() and
165 dreload()].""")
165 dreload()].""")
166 paa('--no-deep-reload',
166 paa('--no-deep-reload',
167 action='store_false', dest='InteractiveShell.deep_reload',
167 action='store_false', dest='InteractiveShell.deep_reload',
168 help="Disable deep (recursive) reloading by default.")
168 help="Disable deep (recursive) reloading by default.")
169 paa('--editor',
169 paa('--editor',
170 type=str, dest='TerminalInteractiveShell.editor',
170 type=str, dest='TerminalInteractiveShell.editor',
171 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
171 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
172 metavar='TerminalInteractiveShell.editor')
172 metavar='TerminalInteractiveShell.editor')
173 paa('--log','-l',
173 paa('--log','-l',
174 action='store_true', dest='InteractiveShell.logstart',
174 action='store_true', dest='InteractiveShell.logstart',
175 help="Start logging to the default log file (./ipython_log.py).")
175 help="Start logging to the default log file (./ipython_log.py).")
176 paa('--logfile','-lf',
176 paa('--logfile','-lf',
177 type=unicode, dest='InteractiveShell.logfile',
177 type=unicode, dest='InteractiveShell.logfile',
178 help="Start logging to logfile with this name.",
178 help="Start logging to logfile with this name.",
179 metavar='InteractiveShell.logfile')
179 metavar='InteractiveShell.logfile')
180 paa('--log-append','-la',
180 paa('--log-append','-la',
181 type=unicode, dest='InteractiveShell.logappend',
181 type=unicode, dest='InteractiveShell.logappend',
182 help="Start logging to the given file in append mode.",
182 help="Start logging to the given file in append mode.",
183 metavar='InteractiveShell.logfile')
183 metavar='InteractiveShell.logfile')
184 paa('--pdb',
184 paa('--pdb',
185 action='store_true', dest='InteractiveShell.pdb',
185 action='store_true', dest='InteractiveShell.pdb',
186 help="Enable auto calling the pdb debugger after every exception.")
186 help="Enable auto calling the pdb debugger after every exception.")
187 paa('--no-pdb',
187 paa('--no-pdb',
188 action='store_false', dest='InteractiveShell.pdb',
188 action='store_false', dest='InteractiveShell.pdb',
189 help="Disable auto calling the pdb debugger after every exception.")
189 help="Disable auto calling the pdb debugger after every exception.")
190 paa('--pprint',
190 paa('--pprint',
191 action='store_true', dest='InteractiveShell.pprint',
191 action='store_true', dest='InteractiveShell.pprint',
192 help="Enable auto pretty printing of results.")
192 help="Enable auto pretty printing of results.")
193 paa('--no-pprint',
193 paa('--no-pprint',
194 action='store_false', dest='InteractiveShell.pprint',
194 action='store_false', dest='InteractiveShell.pprint',
195 help="Disable auto auto pretty printing of results.")
195 help="Disable auto auto pretty printing of results.")
196 paa('--prompt-in1','-pi1',
196 paa('--prompt-in1','-pi1',
197 type=str, dest='InteractiveShell.prompt_in1',
197 type=str, dest='InteractiveShell.prompt_in1',
198 help=
198 help=
199 """Set the main input prompt ('In [\#]: '). Note that if you are using
199 """Set the main input prompt ('In [\#]: '). Note that if you are using
200 numbered prompts, the number is represented with a '\#' in the string.
200 numbered prompts, the number is represented with a '\#' in the string.
201 Don't forget to quote strings with spaces embedded in them. Most
201 Don't forget to quote strings with spaces embedded in them. Most
202 bash-like escapes can be used to customize IPython's prompts, as well
202 bash-like escapes can be used to customize IPython's prompts, as well
203 as a few additional ones which are IPython-spe- cific. All valid
203 as a few additional ones which are IPython-spe- cific. All valid
204 prompt escapes are described in detail in the Customization section of
204 prompt escapes are described in detail in the Customization section of
205 the IPython manual.""",
205 the IPython manual.""",
206 metavar='InteractiveShell.prompt_in1')
206 metavar='InteractiveShell.prompt_in1')
207 paa('--prompt-in2','-pi2',
207 paa('--prompt-in2','-pi2',
208 type=str, dest='InteractiveShell.prompt_in2',
208 type=str, dest='InteractiveShell.prompt_in2',
209 help=
209 help=
210 """Set the secondary input prompt (' .\D.: '). Similar to the previous
210 """Set the secondary input prompt (' .\D.: '). Similar to the previous
211 option, but used for the continuation prompts. The special sequence
211 option, but used for the continuation prompts. The special sequence
212 '\D' is similar to '\#', but with all digits replaced by dots (so you
212 '\D' is similar to '\#', but with all digits replaced by dots (so you
213 can have your continuation prompt aligned with your input prompt).
213 can have your continuation prompt aligned with your input prompt).
214 Default: ' .\D.: ' (note three spaces at the start for alignment with
214 Default: ' .\D.: ' (note three spaces at the start for alignment with
215 'In [\#]')""",
215 'In [\#]')""",
216 metavar='InteractiveShell.prompt_in2')
216 metavar='InteractiveShell.prompt_in2')
217 paa('--prompt-out','-po',
217 paa('--prompt-out','-po',
218 type=str, dest='InteractiveShell.prompt_out',
218 type=str, dest='InteractiveShell.prompt_out',
219 help="Set the output prompt ('Out[\#]:')",
219 help="Set the output prompt ('Out[\#]:')",
220 metavar='InteractiveShell.prompt_out')
220 metavar='InteractiveShell.prompt_out')
221 paa('--quick',
221 paa('--quick',
222 action='store_true', dest='Global.quick',
222 action='store_true', dest='Global.quick',
223 help="Enable quick startup with no config files.")
223 help="Enable quick startup with no config files.")
224 paa('--readline',
224 paa('--readline',
225 action='store_true', dest='InteractiveShell.readline_use',
225 action='store_true', dest='InteractiveShell.readline_use',
226 help="Enable readline for command line usage.")
226 help="Enable readline for command line usage.")
227 paa('--no-readline',
227 paa('--no-readline',
228 action='store_false', dest='InteractiveShell.readline_use',
228 action='store_false', dest='InteractiveShell.readline_use',
229 help="Disable readline for command line usage.")
229 help="Disable readline for command line usage.")
230 paa('--screen-length','-sl',
230 paa('--screen-length','-sl',
231 type=int, dest='TerminalInteractiveShell.screen_length',
231 type=int, dest='TerminalInteractiveShell.screen_length',
232 help=
232 help=
233 """Number of lines of your screen, used to control printing of very
233 """Number of lines of your screen, used to control printing of very
234 long strings. Strings longer than this number of lines will be sent
234 long strings. Strings longer than this number of lines will be sent
235 through a pager instead of directly printed. The default value for
235 through a pager instead of directly printed. The default value for
236 this is 0, which means IPython will auto-detect your screen size every
236 this is 0, which means IPython will auto-detect your screen size every
237 time it needs to print certain potentially long strings (this doesn't
237 time it needs to print certain potentially long strings (this doesn't
238 change the behavior of the 'print' keyword, it's only triggered
238 change the behavior of the 'print' keyword, it's only triggered
239 internally). If for some reason this isn't working well (it needs
239 internally). If for some reason this isn't working well (it needs
240 curses support), specify it yourself. Otherwise don't change the
240 curses support), specify it yourself. Otherwise don't change the
241 default.""",
241 default.""",
242 metavar='TerminalInteractiveShell.screen_length')
242 metavar='TerminalInteractiveShell.screen_length')
243 paa('--separate-in','-si',
243 paa('--separate-in','-si',
244 type=str, dest='InteractiveShell.separate_in',
244 type=str, dest='InteractiveShell.separate_in',
245 help="Separator before input prompts. Default '\\n'.",
245 help="Separator before input prompts. Default '\\n'.",
246 metavar='InteractiveShell.separate_in')
246 metavar='InteractiveShell.separate_in')
247 paa('--separate-out','-so',
247 paa('--separate-out','-so',
248 type=str, dest='InteractiveShell.separate_out',
248 type=str, dest='InteractiveShell.separate_out',
249 help="Separator before output prompts. Default 0 (nothing).",
249 help="Separator before output prompts. Default 0 (nothing).",
250 metavar='InteractiveShell.separate_out')
250 metavar='InteractiveShell.separate_out')
251 paa('--separate-out2','-so2',
251 paa('--separate-out2','-so2',
252 type=str, dest='InteractiveShell.separate_out2',
252 type=str, dest='InteractiveShell.separate_out2',
253 help="Separator after output prompts. Default 0 (nonight).",
253 help="Separator after output prompts. Default 0 (nonight).",
254 metavar='InteractiveShell.separate_out2')
254 metavar='InteractiveShell.separate_out2')
255 paa('--no-sep',
255 paa('--no-sep',
256 action='store_true', dest='Global.nosep',
256 action='store_true', dest='Global.nosep',
257 help="Eliminate all spacing between prompts.")
257 help="Eliminate all spacing between prompts.")
258 paa('--term-title',
258 paa('--term-title',
259 action='store_true', dest='TerminalInteractiveShell.term_title',
259 action='store_true', dest='TerminalInteractiveShell.term_title',
260 help="Enable auto setting the terminal title.")
260 help="Enable auto setting the terminal title.")
261 paa('--no-term-title',
261 paa('--no-term-title',
262 action='store_false', dest='TerminalInteractiveShell.term_title',
262 action='store_false', dest='TerminalInteractiveShell.term_title',
263 help="Disable auto setting the terminal title.")
263 help="Disable auto setting the terminal title.")
264 paa('--xmode',
264 paa('--xmode',
265 type=str, dest='InteractiveShell.xmode',
265 type=str, dest='InteractiveShell.xmode',
266 help=
266 help=
267 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
267 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
268 similar to python's normal traceback printing. Context: prints 5 lines
268 similar to python's normal traceback printing. Context: prints 5 lines
269 of context source code around each line in the traceback. Verbose:
269 of context source code around each line in the traceback. Verbose:
270 similar to Context, but additionally prints the variables currently
270 similar to Context, but additionally prints the variables currently
271 visible where the exception happened (shortening their strings if too
271 visible where the exception happened (shortening their strings if too
272 long). This can potentially be very slow, if you happen to have a huge
272 long). This can potentially be very slow, if you happen to have a huge
273 data structure whose string representation is complex to compute.
273 data structure whose string representation is complex to compute.
274 Your computer may appear to freeze for a while with cpu usage at 100%%.
274 Your computer may appear to freeze for a while with cpu usage at 100%%.
275 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
275 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
276 it more than once).
276 it more than once).
277 """,
277 """,
278 metavar='InteractiveShell.xmode')
278 metavar='InteractiveShell.xmode')
279 paa('--ext',
279 paa('--ext',
280 type=str, dest='Global.extra_extension',
280 type=str, dest='Global.extra_extension',
281 help="The dotted module name of an IPython extension to load.",
281 help="The dotted module name of an IPython extension to load.",
282 metavar='Global.extra_extension')
282 metavar='Global.extra_extension')
283 paa('-c',
283 paa('-c',
284 type=str, dest='Global.code_to_run',
284 type=str, dest='Global.code_to_run',
285 help="Execute the given command string.",
285 help="Execute the given command string.",
286 metavar='Global.code_to_run')
286 metavar='Global.code_to_run')
287 paa('-i',
287 paa('-i',
288 action='store_true', dest='Global.force_interact',
288 action='store_true', dest='Global.force_interact',
289 help=
289 help=
290 "If running code from the command line, become interactive afterwards.")
290 "If running code from the command line, become interactive afterwards.")
291
291
292 # Options to start with GUI control enabled from the beginning
292 # Options to start with GUI control enabled from the beginning
293 paa('--gui',
293 paa('--gui',
294 type=str, dest='Global.gui',
294 type=str, dest='Global.gui',
295 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
295 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
296 metavar='gui-mode')
296 metavar='gui-mode')
297 paa('--pylab','-pylab',
297 paa('--pylab','-pylab',
298 type=str, dest='Global.pylab',
298 type=str, dest='Global.pylab',
299 nargs='?', const='auto', metavar='gui-mode',
299 nargs='?', const='auto', metavar='gui-mode',
300 help="Pre-load matplotlib and numpy for interactive use. "+
300 help="Pre-load matplotlib and numpy for interactive use. "+
301 "If no value is given, the gui backend is matplotlib's, else use "+
301 "If no value is given, the gui backend is matplotlib's, else use "+
302 "one of: ['tk', 'qt', 'wx', 'gtk'].")
302 "one of: ['tk', 'qt', 'wx', 'gtk'].")
303
303
304 # Legacy GUI options. Leave them in for backwards compatibility, but the
304 # Legacy GUI options. Leave them in for backwards compatibility, but the
305 # 'thread' names are really a misnomer now.
305 # 'thread' names are really a misnomer now.
306 paa('--wthread', '-wthread',
306 paa('--wthread', '-wthread',
307 action='store_true', dest='Global.wthread',
307 action='store_true', dest='Global.wthread',
308 help=
308 help=
309 """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""")
309 """Enable wxPython event loop integration. (DEPRECATED, use --gui wx)""")
310 paa('--q4thread', '--qthread', '-q4thread', '-qthread',
310 paa('--q4thread', '--qthread', '-q4thread', '-qthread',
311 action='store_true', dest='Global.q4thread',
311 action='store_true', dest='Global.q4thread',
312 help=
312 help=
313 """Enable Qt4 event loop integration. Qt3 is no longer supported.
313 """Enable Qt4 event loop integration. Qt3 is no longer supported.
314 (DEPRECATED, use --gui qt)""")
314 (DEPRECATED, use --gui qt)""")
315 paa('--gthread', '-gthread',
315 paa('--gthread', '-gthread',
316 action='store_true', dest='Global.gthread',
316 action='store_true', dest='Global.gthread',
317 help=
317 help=
318 """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""")
318 """Enable GTK event loop integration. (DEPRECATED, use --gui gtk)""")
319
319
320
320
321 #-----------------------------------------------------------------------------
321 #-----------------------------------------------------------------------------
322 # Crash handler for this application
322 # Crash handler for this application
323 #-----------------------------------------------------------------------------
323 #-----------------------------------------------------------------------------
324
324
325
326 _message_template = """\
325 _message_template = """\
327 Oops, $self.app_name crashed. We do our best to make it stable, but...
326 Oops, $self.app_name crashed. We do our best to make it stable, but...
328
327
329 A crash report was automatically generated with the following information:
328 A crash report was automatically generated with the following information:
330 - A verbatim copy of the crash traceback.
329 - A verbatim copy of the crash traceback.
331 - A copy of your input history during this session.
330 - A copy of your input history during this session.
332 - Data on your current $self.app_name configuration.
331 - Data on your current $self.app_name configuration.
333
332
334 It was left in the file named:
333 It was left in the file named:
335 \t'$self.crash_report_fname'
334 \t'$self.crash_report_fname'
336 If you can email this file to the developers, the information in it will help
335 If you can email this file to the developers, the information in it will help
337 them in understanding and correcting the problem.
336 them in understanding and correcting the problem.
338
337
339 You can mail it to: $self.contact_name at $self.contact_email
338 You can mail it to: $self.contact_name at $self.contact_email
340 with the subject '$self.app_name Crash Report'.
339 with the subject '$self.app_name Crash Report'.
341
340
342 If you want to do it now, the following command will work (under Unix):
341 If you want to do it now, the following command will work (under Unix):
343 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
342 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
344
343
345 To ensure accurate tracking of this issue, please file a report about it at:
344 To ensure accurate tracking of this issue, please file a report about it at:
346 $self.bug_tracker
345 $self.bug_tracker
347 """
346 """
348
347
349 class IPAppCrashHandler(CrashHandler):
348 class IPAppCrashHandler(CrashHandler):
350 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
349 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
351
350
352 message_template = _message_template
351 message_template = _message_template
353
352
354 def __init__(self, app):
353 def __init__(self, app):
355 contact_name = release.authors['Fernando'][0]
354 contact_name = release.authors['Fernando'][0]
356 contact_email = release.authors['Fernando'][1]
355 contact_email = release.authors['Fernando'][1]
357 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
356 bug_tracker = 'http://github.com/ipython/ipython/issues'
358 super(IPAppCrashHandler,self).__init__(
357 super(IPAppCrashHandler,self).__init__(
359 app, contact_name, contact_email, bug_tracker
358 app, contact_name, contact_email, bug_tracker
360 )
359 )
361
360
362 def make_report(self,traceback):
361 def make_report(self,traceback):
363 """Return a string containing a crash report."""
362 """Return a string containing a crash report."""
364
363
365 sec_sep = self.section_sep
364 sec_sep = self.section_sep
366 # Start with parent report
365 # Start with parent report
367 report = [super(IPAppCrashHandler, self).make_report(traceback)]
366 report = [super(IPAppCrashHandler, self).make_report(traceback)]
368 # Add interactive-specific info we may have
367 # Add interactive-specific info we may have
369 rpt_add = report.append
368 rpt_add = report.append
370 try:
369 try:
371 rpt_add(sec_sep+"History of session input:")
370 rpt_add(sec_sep+"History of session input:")
372 for line in self.app.shell.user_ns['_ih']:
371 for line in self.app.shell.user_ns['_ih']:
373 rpt_add(line)
372 rpt_add(line)
374 rpt_add('\n*** Last line of input (may not be in above history):\n')
373 rpt_add('\n*** Last line of input (may not be in above history):\n')
375 rpt_add(self.app.shell._last_input_line+'\n')
374 rpt_add(self.app.shell._last_input_line+'\n')
376 except:
375 except:
377 pass
376 pass
378
377
379 return ''.join(report)
378 return ''.join(report)
380
379
381
380
382 #-----------------------------------------------------------------------------
381 #-----------------------------------------------------------------------------
383 # Main classes and functions
382 # Main classes and functions
384 #-----------------------------------------------------------------------------
383 #-----------------------------------------------------------------------------
385
384
386 class IPythonApp(Application):
385 class IPythonApp(Application):
387 name = u'ipython'
386 name = u'ipython'
388 #: argparse formats better the 'usage' than the 'description' field
387 #: argparse formats better the 'usage' than the 'description' field
389 description = None
388 description = None
390 usage = usage.cl_usage
389 usage = usage.cl_usage
391 command_line_loader = IPAppConfigLoader
390 command_line_loader = IPAppConfigLoader
392 default_config_file_name = default_config_file_name
391 default_config_file_name = default_config_file_name
393 crash_handler_class = IPAppCrashHandler
392 crash_handler_class = IPAppCrashHandler
394
393
395 def create_default_config(self):
394 def create_default_config(self):
396 super(IPythonApp, self).create_default_config()
395 super(IPythonApp, self).create_default_config()
397 # Eliminate multiple lookups
396 # Eliminate multiple lookups
398 Global = self.default_config.Global
397 Global = self.default_config.Global
399
398
400 # Set all default values
399 # Set all default values
401 Global.display_banner = True
400 Global.display_banner = True
402
401
403 # If the -c flag is given or a file is given to run at the cmd line
402 # If the -c flag is given or a file is given to run at the cmd line
404 # like "ipython foo.py", normally we exit without starting the main
403 # like "ipython foo.py", normally we exit without starting the main
405 # loop. The force_interact config variable allows a user to override
404 # loop. The force_interact config variable allows a user to override
406 # this and interact. It is also set by the -i cmd line flag, just
405 # this and interact. It is also set by the -i cmd line flag, just
407 # like Python.
406 # like Python.
408 Global.force_interact = False
407 Global.force_interact = False
409
408
410 # By default always interact by starting the IPython mainloop.
409 # By default always interact by starting the IPython mainloop.
411 Global.interact = True
410 Global.interact = True
412
411
413 # No GUI integration by default
412 # No GUI integration by default
414 Global.gui = False
413 Global.gui = False
415 # Pylab off by default
414 # Pylab off by default
416 Global.pylab = False
415 Global.pylab = False
417
416
418 # Deprecated versions of gui support that used threading, we support
417 # Deprecated versions of gui support that used threading, we support
419 # them just for bacwards compatibility as an alternate spelling for
418 # them just for bacwards compatibility as an alternate spelling for
420 # '--gui X'
419 # '--gui X'
421 Global.qthread = False
420 Global.qthread = False
422 Global.q4thread = False
421 Global.q4thread = False
423 Global.wthread = False
422 Global.wthread = False
424 Global.gthread = False
423 Global.gthread = False
425
424
426 def load_file_config(self):
425 def load_file_config(self):
427 if hasattr(self.command_line_config.Global, 'quick'):
426 if hasattr(self.command_line_config.Global, 'quick'):
428 if self.command_line_config.Global.quick:
427 if self.command_line_config.Global.quick:
429 self.file_config = Config()
428 self.file_config = Config()
430 return
429 return
431 super(IPythonApp, self).load_file_config()
430 super(IPythonApp, self).load_file_config()
432
431
433 def post_load_file_config(self):
432 def post_load_file_config(self):
434 if hasattr(self.command_line_config.Global, 'extra_extension'):
433 if hasattr(self.command_line_config.Global, 'extra_extension'):
435 if not hasattr(self.file_config.Global, 'extensions'):
434 if not hasattr(self.file_config.Global, 'extensions'):
436 self.file_config.Global.extensions = []
435 self.file_config.Global.extensions = []
437 self.file_config.Global.extensions.append(
436 self.file_config.Global.extensions.append(
438 self.command_line_config.Global.extra_extension)
437 self.command_line_config.Global.extra_extension)
439 del self.command_line_config.Global.extra_extension
438 del self.command_line_config.Global.extra_extension
440
439
441 def pre_construct(self):
440 def pre_construct(self):
442 config = self.master_config
441 config = self.master_config
443
442
444 if hasattr(config.Global, 'classic'):
443 if hasattr(config.Global, 'classic'):
445 if config.Global.classic:
444 if config.Global.classic:
446 config.InteractiveShell.cache_size = 0
445 config.InteractiveShell.cache_size = 0
447 config.InteractiveShell.pprint = 0
446 config.InteractiveShell.pprint = 0
448 config.InteractiveShell.prompt_in1 = '>>> '
447 config.InteractiveShell.prompt_in1 = '>>> '
449 config.InteractiveShell.prompt_in2 = '... '
448 config.InteractiveShell.prompt_in2 = '... '
450 config.InteractiveShell.prompt_out = ''
449 config.InteractiveShell.prompt_out = ''
451 config.InteractiveShell.separate_in = \
450 config.InteractiveShell.separate_in = \
452 config.InteractiveShell.separate_out = \
451 config.InteractiveShell.separate_out = \
453 config.InteractiveShell.separate_out2 = ''
452 config.InteractiveShell.separate_out2 = ''
454 config.InteractiveShell.colors = 'NoColor'
453 config.InteractiveShell.colors = 'NoColor'
455 config.InteractiveShell.xmode = 'Plain'
454 config.InteractiveShell.xmode = 'Plain'
456
455
457 if hasattr(config.Global, 'nosep'):
456 if hasattr(config.Global, 'nosep'):
458 if config.Global.nosep:
457 if config.Global.nosep:
459 config.InteractiveShell.separate_in = \
458 config.InteractiveShell.separate_in = \
460 config.InteractiveShell.separate_out = \
459 config.InteractiveShell.separate_out = \
461 config.InteractiveShell.separate_out2 = ''
460 config.InteractiveShell.separate_out2 = ''
462
461
463 # if there is code of files to run from the cmd line, don't interact
462 # if there is code of files to run from the cmd line, don't interact
464 # unless the -i flag (Global.force_interact) is true.
463 # unless the -i flag (Global.force_interact) is true.
465 code_to_run = config.Global.get('code_to_run','')
464 code_to_run = config.Global.get('code_to_run','')
466 file_to_run = False
465 file_to_run = False
467 if self.extra_args and self.extra_args[0]:
466 if self.extra_args and self.extra_args[0]:
468 file_to_run = True
467 file_to_run = True
469 if file_to_run or code_to_run:
468 if file_to_run or code_to_run:
470 if not config.Global.force_interact:
469 if not config.Global.force_interact:
471 config.Global.interact = False
470 config.Global.interact = False
472
471
473 def construct(self):
472 def construct(self):
474 # I am a little hesitant to put these into InteractiveShell itself.
473 # I am a little hesitant to put these into InteractiveShell itself.
475 # But that might be the place for them
474 # But that might be the place for them
476 sys.path.insert(0, '')
475 sys.path.insert(0, '')
477
476
478 # Create an InteractiveShell instance.
477 # Create an InteractiveShell instance.
479 self.shell = TerminalInteractiveShell.instance(config=self.master_config)
478 self.shell = TerminalInteractiveShell.instance(config=self.master_config)
480
479
481 def post_construct(self):
480 def post_construct(self):
482 """Do actions after construct, but before starting the app."""
481 """Do actions after construct, but before starting the app."""
483 config = self.master_config
482 config = self.master_config
484
483
485 # shell.display_banner should always be False for the terminal
484 # shell.display_banner should always be False for the terminal
486 # based app, because we call shell.show_banner() by hand below
485 # based app, because we call shell.show_banner() by hand below
487 # so the banner shows *before* all extension loading stuff.
486 # so the banner shows *before* all extension loading stuff.
488 self.shell.display_banner = False
487 self.shell.display_banner = False
489 if config.Global.display_banner and \
488 if config.Global.display_banner and \
490 config.Global.interact:
489 config.Global.interact:
491 self.shell.show_banner()
490 self.shell.show_banner()
492
491
493 # Make sure there is a space below the banner.
492 # Make sure there is a space below the banner.
494 if self.log_level <= logging.INFO: print
493 if self.log_level <= logging.INFO: print
495
494
496 # Now a variety of things that happen after the banner is printed.
495 # Now a variety of things that happen after the banner is printed.
497 self._enable_gui_pylab()
496 self._enable_gui_pylab()
498 self._load_extensions()
497 self._load_extensions()
499 self._run_exec_lines()
498 self._run_exec_lines()
500 self._run_exec_files()
499 self._run_exec_files()
501 self._run_cmd_line_code()
500 self._run_cmd_line_code()
502
501
503 def _enable_gui_pylab(self):
502 def _enable_gui_pylab(self):
504 """Enable GUI event loop integration, taking pylab into account."""
503 """Enable GUI event loop integration, taking pylab into account."""
505 Global = self.master_config.Global
504 Global = self.master_config.Global
506
505
507 # Select which gui to use
506 # Select which gui to use
508 if Global.gui:
507 if Global.gui:
509 gui = Global.gui
508 gui = Global.gui
510 # The following are deprecated, but there's likely to be a lot of use
509 # The following are deprecated, but there's likely to be a lot of use
511 # of this form out there, so we might as well support it for now. But
510 # of this form out there, so we might as well support it for now. But
512 # the --gui option above takes precedence.
511 # the --gui option above takes precedence.
513 elif Global.wthread:
512 elif Global.wthread:
514 gui = inputhook.GUI_WX
513 gui = inputhook.GUI_WX
515 elif Global.qthread:
514 elif Global.qthread:
516 gui = inputhook.GUI_QT
515 gui = inputhook.GUI_QT
517 elif Global.gthread:
516 elif Global.gthread:
518 gui = inputhook.GUI_GTK
517 gui = inputhook.GUI_GTK
519 else:
518 else:
520 gui = None
519 gui = None
521
520
522 # Using --pylab will also require gui activation, though which toolkit
521 # Using --pylab will also require gui activation, though which toolkit
523 # to use may be chosen automatically based on mpl configuration.
522 # to use may be chosen automatically based on mpl configuration.
524 if Global.pylab:
523 if Global.pylab:
525 activate = self.shell.enable_pylab
524 activate = self.shell.enable_pylab
526 if Global.pylab == 'auto':
525 if Global.pylab == 'auto':
527 gui = None
526 gui = None
528 else:
527 else:
529 gui = Global.pylab
528 gui = Global.pylab
530 else:
529 else:
531 # Enable only GUI integration, no pylab
530 # Enable only GUI integration, no pylab
532 activate = inputhook.enable_gui
531 activate = inputhook.enable_gui
533
532
534 if gui or Global.pylab:
533 if gui or Global.pylab:
535 try:
534 try:
536 self.log.info("Enabling GUI event loop integration, "
535 self.log.info("Enabling GUI event loop integration, "
537 "toolkit=%s, pylab=%s" % (gui, Global.pylab) )
536 "toolkit=%s, pylab=%s" % (gui, Global.pylab) )
538 activate(gui)
537 activate(gui)
539 except:
538 except:
540 self.log.warn("Error in enabling GUI event loop integration:")
539 self.log.warn("Error in enabling GUI event loop integration:")
541 self.shell.showtraceback()
540 self.shell.showtraceback()
542
541
543 def _load_extensions(self):
542 def _load_extensions(self):
544 """Load all IPython extensions in Global.extensions.
543 """Load all IPython extensions in Global.extensions.
545
544
546 This uses the :meth:`ExtensionManager.load_extensions` to load all
545 This uses the :meth:`ExtensionManager.load_extensions` to load all
547 the extensions listed in ``self.master_config.Global.extensions``.
546 the extensions listed in ``self.master_config.Global.extensions``.
548 """
547 """
549 try:
548 try:
550 if hasattr(self.master_config.Global, 'extensions'):
549 if hasattr(self.master_config.Global, 'extensions'):
551 self.log.debug("Loading IPython extensions...")
550 self.log.debug("Loading IPython extensions...")
552 extensions = self.master_config.Global.extensions
551 extensions = self.master_config.Global.extensions
553 for ext in extensions:
552 for ext in extensions:
554 try:
553 try:
555 self.log.info("Loading IPython extension: %s" % ext)
554 self.log.info("Loading IPython extension: %s" % ext)
556 self.shell.extension_manager.load_extension(ext)
555 self.shell.extension_manager.load_extension(ext)
557 except:
556 except:
558 self.log.warn("Error in loading extension: %s" % ext)
557 self.log.warn("Error in loading extension: %s" % ext)
559 self.shell.showtraceback()
558 self.shell.showtraceback()
560 except:
559 except:
561 self.log.warn("Unknown error in loading extensions:")
560 self.log.warn("Unknown error in loading extensions:")
562 self.shell.showtraceback()
561 self.shell.showtraceback()
563
562
564 def _run_exec_lines(self):
563 def _run_exec_lines(self):
565 """Run lines of code in Global.exec_lines in the user's namespace."""
564 """Run lines of code in Global.exec_lines in the user's namespace."""
566 try:
565 try:
567 if hasattr(self.master_config.Global, 'exec_lines'):
566 if hasattr(self.master_config.Global, 'exec_lines'):
568 self.log.debug("Running code from Global.exec_lines...")
567 self.log.debug("Running code from Global.exec_lines...")
569 exec_lines = self.master_config.Global.exec_lines
568 exec_lines = self.master_config.Global.exec_lines
570 for line in exec_lines:
569 for line in exec_lines:
571 try:
570 try:
572 self.log.info("Running code in user namespace: %s" %
571 self.log.info("Running code in user namespace: %s" %
573 line)
572 line)
574 self.shell.run_cell(line)
573 self.shell.run_cell(line)
575 except:
574 except:
576 self.log.warn("Error in executing line in user "
575 self.log.warn("Error in executing line in user "
577 "namespace: %s" % line)
576 "namespace: %s" % line)
578 self.shell.showtraceback()
577 self.shell.showtraceback()
579 except:
578 except:
580 self.log.warn("Unknown error in handling Global.exec_lines:")
579 self.log.warn("Unknown error in handling Global.exec_lines:")
581 self.shell.showtraceback()
580 self.shell.showtraceback()
582
581
583 def _exec_file(self, fname):
582 def _exec_file(self, fname):
584 full_filename = filefind(fname, [u'.', self.ipython_dir])
583 full_filename = filefind(fname, [u'.', self.ipython_dir])
585 if os.path.isfile(full_filename):
584 if os.path.isfile(full_filename):
586 if full_filename.endswith(u'.py'):
585 if full_filename.endswith(u'.py'):
587 self.log.info("Running file in user namespace: %s" %
586 self.log.info("Running file in user namespace: %s" %
588 full_filename)
587 full_filename)
589 # Ensure that __file__ is always defined to match Python behavior
588 # Ensure that __file__ is always defined to match Python behavior
590 self.shell.user_ns['__file__'] = fname
589 self.shell.user_ns['__file__'] = fname
591 try:
590 try:
592 self.shell.safe_execfile(full_filename, self.shell.user_ns)
591 self.shell.safe_execfile(full_filename, self.shell.user_ns)
593 finally:
592 finally:
594 del self.shell.user_ns['__file__']
593 del self.shell.user_ns['__file__']
595 elif full_filename.endswith('.ipy'):
594 elif full_filename.endswith('.ipy'):
596 self.log.info("Running file in user namespace: %s" %
595 self.log.info("Running file in user namespace: %s" %
597 full_filename)
596 full_filename)
598 self.shell.safe_execfile_ipy(full_filename)
597 self.shell.safe_execfile_ipy(full_filename)
599 else:
598 else:
600 self.log.warn("File does not have a .py or .ipy extension: <%s>"
599 self.log.warn("File does not have a .py or .ipy extension: <%s>"
601 % full_filename)
600 % full_filename)
602 def _run_exec_files(self):
601 def _run_exec_files(self):
603 try:
602 try:
604 if hasattr(self.master_config.Global, 'exec_files'):
603 if hasattr(self.master_config.Global, 'exec_files'):
605 self.log.debug("Running files in Global.exec_files...")
604 self.log.debug("Running files in Global.exec_files...")
606 exec_files = self.master_config.Global.exec_files
605 exec_files = self.master_config.Global.exec_files
607 for fname in exec_files:
606 for fname in exec_files:
608 self._exec_file(fname)
607 self._exec_file(fname)
609 except:
608 except:
610 self.log.warn("Unknown error in handling Global.exec_files:")
609 self.log.warn("Unknown error in handling Global.exec_files:")
611 self.shell.showtraceback()
610 self.shell.showtraceback()
612
611
613 def _run_cmd_line_code(self):
612 def _run_cmd_line_code(self):
614 if hasattr(self.master_config.Global, 'code_to_run'):
613 if hasattr(self.master_config.Global, 'code_to_run'):
615 line = self.master_config.Global.code_to_run
614 line = self.master_config.Global.code_to_run
616 try:
615 try:
617 self.log.info("Running code given at command line (-c): %s" %
616 self.log.info("Running code given at command line (-c): %s" %
618 line)
617 line)
619 self.shell.run_cell(line)
618 self.shell.run_cell(line)
620 except:
619 except:
621 self.log.warn("Error in executing line in user namespace: %s" %
620 self.log.warn("Error in executing line in user namespace: %s" %
622 line)
621 line)
623 self.shell.showtraceback()
622 self.shell.showtraceback()
624 return
623 return
625 # Like Python itself, ignore the second if the first of these is present
624 # Like Python itself, ignore the second if the first of these is present
626 try:
625 try:
627 fname = self.extra_args[0]
626 fname = self.extra_args[0]
628 except:
627 except:
629 pass
628 pass
630 else:
629 else:
631 try:
630 try:
632 self._exec_file(fname)
631 self._exec_file(fname)
633 except:
632 except:
634 self.log.warn("Error in executing file in user namespace: %s" %
633 self.log.warn("Error in executing file in user namespace: %s" %
635 fname)
634 fname)
636 self.shell.showtraceback()
635 self.shell.showtraceback()
637
636
638 def start_app(self):
637 def start_app(self):
639 if self.master_config.Global.interact:
638 if self.master_config.Global.interact:
640 self.log.debug("Starting IPython's mainloop...")
639 self.log.debug("Starting IPython's mainloop...")
641 self.shell.mainloop()
640 self.shell.mainloop()
642 else:
641 else:
643 self.log.debug("IPython not interactive, start_app is no-op...")
642 self.log.debug("IPython not interactive, start_app is no-op...")
644
643
645
644
646 def load_default_config(ipython_dir=None):
645 def load_default_config(ipython_dir=None):
647 """Load the default config file from the default ipython_dir.
646 """Load the default config file from the default ipython_dir.
648
647
649 This is useful for embedded shells.
648 This is useful for embedded shells.
650 """
649 """
651 if ipython_dir is None:
650 if ipython_dir is None:
652 ipython_dir = get_ipython_dir()
651 ipython_dir = get_ipython_dir()
653 cl = PyFileConfigLoader(default_config_file_name, ipython_dir)
652 cl = PyFileConfigLoader(default_config_file_name, ipython_dir)
654 config = cl.load_config()
653 config = cl.load_config()
655 return config
654 return config
656
655
657
656
658 def launch_new_instance():
657 def launch_new_instance():
659 """Create and run a full blown IPython instance"""
658 """Create and run a full blown IPython instance"""
660 app = IPythonApp()
659 app = IPythonApp()
661 app.start()
660 app.start()
662
661
663
662
664 if __name__ == '__main__':
663 if __name__ == '__main__':
665 launch_new_instance()
664 launch_new_instance()
@@ -1,538 +1,538 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython cluster directory
4 The IPython cluster directory
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 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 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import os
20 import os
21 import shutil
21 import shutil
22 import sys
22 import sys
23 import warnings
23 import warnings
24
24
25 from twisted.python import log
25 from twisted.python import log
26
26
27 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.core.crashhandler import CrashHandler
30 from IPython.core.crashhandler import CrashHandler
31 from IPython.core import release
31 from IPython.core import release
32 from IPython.utils.path import (
32 from IPython.utils.path import (
33 get_ipython_package_dir,
33 get_ipython_package_dir,
34 expand_path
34 expand_path
35 )
35 )
36 from IPython.utils.traitlets import Unicode
36 from IPython.utils.traitlets import Unicode
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Warnings control
39 # Warnings control
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Twisted generates annoying warnings with Python 2.6, as will do other code
41 # Twisted generates annoying warnings with Python 2.6, as will do other code
42 # that imports 'sets' as of today
42 # that imports 'sets' as of today
43 warnings.filterwarnings('ignore', 'the sets module is deprecated',
43 warnings.filterwarnings('ignore', 'the sets module is deprecated',
44 DeprecationWarning )
44 DeprecationWarning )
45
45
46 # This one also comes from Twisted
46 # This one also comes from Twisted
47 warnings.filterwarnings('ignore', 'the sha module is deprecated',
47 warnings.filterwarnings('ignore', 'the sha module is deprecated',
48 DeprecationWarning)
48 DeprecationWarning)
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Module errors
51 # Module errors
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 class ClusterDirError(Exception):
54 class ClusterDirError(Exception):
55 pass
55 pass
56
56
57
57
58 class PIDFileError(Exception):
58 class PIDFileError(Exception):
59 pass
59 pass
60
60
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Class for managing cluster directories
63 # Class for managing cluster directories
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class ClusterDir(Configurable):
66 class ClusterDir(Configurable):
67 """An object to manage the cluster directory and its resources.
67 """An object to manage the cluster directory and its resources.
68
68
69 The cluster directory is used by :command:`ipcontroller`,
69 The cluster directory is used by :command:`ipcontroller`,
70 :command:`ipcontroller` and :command:`ipcontroller` to manage the
70 :command:`ipcontroller` and :command:`ipcontroller` to manage the
71 configuration, logging and security of these applications.
71 configuration, logging and security of these applications.
72
72
73 This object knows how to find, create and manage these directories. This
73 This object knows how to find, create and manage these directories. This
74 should be used by any code that want's to handle cluster directories.
74 should be used by any code that want's to handle cluster directories.
75 """
75 """
76
76
77 security_dir_name = Unicode('security')
77 security_dir_name = Unicode('security')
78 log_dir_name = Unicode('log')
78 log_dir_name = Unicode('log')
79 pid_dir_name = Unicode('pid')
79 pid_dir_name = Unicode('pid')
80 security_dir = Unicode(u'')
80 security_dir = Unicode(u'')
81 log_dir = Unicode(u'')
81 log_dir = Unicode(u'')
82 pid_dir = Unicode(u'')
82 pid_dir = Unicode(u'')
83 location = Unicode(u'')
83 location = Unicode(u'')
84
84
85 def __init__(self, location=u''):
85 def __init__(self, location=u''):
86 super(ClusterDir, self).__init__(location=location)
86 super(ClusterDir, self).__init__(location=location)
87
87
88 def _location_changed(self, name, old, new):
88 def _location_changed(self, name, old, new):
89 if not os.path.isdir(new):
89 if not os.path.isdir(new):
90 os.makedirs(new)
90 os.makedirs(new)
91 self.security_dir = os.path.join(new, self.security_dir_name)
91 self.security_dir = os.path.join(new, self.security_dir_name)
92 self.log_dir = os.path.join(new, self.log_dir_name)
92 self.log_dir = os.path.join(new, self.log_dir_name)
93 self.pid_dir = os.path.join(new, self.pid_dir_name)
93 self.pid_dir = os.path.join(new, self.pid_dir_name)
94 self.check_dirs()
94 self.check_dirs()
95
95
96 def _log_dir_changed(self, name, old, new):
96 def _log_dir_changed(self, name, old, new):
97 self.check_log_dir()
97 self.check_log_dir()
98
98
99 def check_log_dir(self):
99 def check_log_dir(self):
100 if not os.path.isdir(self.log_dir):
100 if not os.path.isdir(self.log_dir):
101 os.mkdir(self.log_dir)
101 os.mkdir(self.log_dir)
102
102
103 def _security_dir_changed(self, name, old, new):
103 def _security_dir_changed(self, name, old, new):
104 self.check_security_dir()
104 self.check_security_dir()
105
105
106 def check_security_dir(self):
106 def check_security_dir(self):
107 if not os.path.isdir(self.security_dir):
107 if not os.path.isdir(self.security_dir):
108 os.mkdir(self.security_dir, 0700)
108 os.mkdir(self.security_dir, 0700)
109 os.chmod(self.security_dir, 0700)
109 os.chmod(self.security_dir, 0700)
110
110
111 def _pid_dir_changed(self, name, old, new):
111 def _pid_dir_changed(self, name, old, new):
112 self.check_pid_dir()
112 self.check_pid_dir()
113
113
114 def check_pid_dir(self):
114 def check_pid_dir(self):
115 if not os.path.isdir(self.pid_dir):
115 if not os.path.isdir(self.pid_dir):
116 os.mkdir(self.pid_dir, 0700)
116 os.mkdir(self.pid_dir, 0700)
117 os.chmod(self.pid_dir, 0700)
117 os.chmod(self.pid_dir, 0700)
118
118
119 def check_dirs(self):
119 def check_dirs(self):
120 self.check_security_dir()
120 self.check_security_dir()
121 self.check_log_dir()
121 self.check_log_dir()
122 self.check_pid_dir()
122 self.check_pid_dir()
123
123
124 def load_config_file(self, filename):
124 def load_config_file(self, filename):
125 """Load a config file from the top level of the cluster dir.
125 """Load a config file from the top level of the cluster dir.
126
126
127 Parameters
127 Parameters
128 ----------
128 ----------
129 filename : unicode or str
129 filename : unicode or str
130 The filename only of the config file that must be located in
130 The filename only of the config file that must be located in
131 the top-level of the cluster directory.
131 the top-level of the cluster directory.
132 """
132 """
133 loader = PyFileConfigLoader(filename, self.location)
133 loader = PyFileConfigLoader(filename, self.location)
134 return loader.load_config()
134 return loader.load_config()
135
135
136 def copy_config_file(self, config_file, path=None, overwrite=False):
136 def copy_config_file(self, config_file, path=None, overwrite=False):
137 """Copy a default config file into the active cluster directory.
137 """Copy a default config file into the active cluster directory.
138
138
139 Default configuration files are kept in :mod:`IPython.config.default`.
139 Default configuration files are kept in :mod:`IPython.config.default`.
140 This function moves these from that location to the working cluster
140 This function moves these from that location to the working cluster
141 directory.
141 directory.
142 """
142 """
143 if path is None:
143 if path is None:
144 import IPython.config.default
144 import IPython.config.default
145 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
145 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
146 path = os.path.sep.join(path)
146 path = os.path.sep.join(path)
147 src = os.path.join(path, config_file)
147 src = os.path.join(path, config_file)
148 dst = os.path.join(self.location, config_file)
148 dst = os.path.join(self.location, config_file)
149 if not os.path.isfile(dst) or overwrite:
149 if not os.path.isfile(dst) or overwrite:
150 shutil.copy(src, dst)
150 shutil.copy(src, dst)
151
151
152 def copy_all_config_files(self, path=None, overwrite=False):
152 def copy_all_config_files(self, path=None, overwrite=False):
153 """Copy all config files into the active cluster directory."""
153 """Copy all config files into the active cluster directory."""
154 for f in [u'ipcontroller_config.py', u'ipengine_config.py',
154 for f in [u'ipcontroller_config.py', u'ipengine_config.py',
155 u'ipcluster_config.py']:
155 u'ipcluster_config.py']:
156 self.copy_config_file(f, path=path, overwrite=overwrite)
156 self.copy_config_file(f, path=path, overwrite=overwrite)
157
157
158 @classmethod
158 @classmethod
159 def create_cluster_dir(csl, cluster_dir):
159 def create_cluster_dir(csl, cluster_dir):
160 """Create a new cluster directory given a full path.
160 """Create a new cluster directory given a full path.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 cluster_dir : str
164 cluster_dir : str
165 The full path to the cluster directory. If it does exist, it will
165 The full path to the cluster directory. If it does exist, it will
166 be used. If not, it will be created.
166 be used. If not, it will be created.
167 """
167 """
168 return ClusterDir(location=cluster_dir)
168 return ClusterDir(location=cluster_dir)
169
169
170 @classmethod
170 @classmethod
171 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
171 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
172 """Create a cluster dir by profile name and path.
172 """Create a cluster dir by profile name and path.
173
173
174 Parameters
174 Parameters
175 ----------
175 ----------
176 path : str
176 path : str
177 The path (directory) to put the cluster directory in.
177 The path (directory) to put the cluster directory in.
178 profile : str
178 profile : str
179 The name of the profile. The name of the cluster directory will
179 The name of the profile. The name of the cluster directory will
180 be "cluster_<profile>".
180 be "cluster_<profile>".
181 """
181 """
182 if not os.path.isdir(path):
182 if not os.path.isdir(path):
183 raise ClusterDirError('Directory not found: %s' % path)
183 raise ClusterDirError('Directory not found: %s' % path)
184 cluster_dir = os.path.join(path, u'cluster_' + profile)
184 cluster_dir = os.path.join(path, u'cluster_' + profile)
185 return ClusterDir(location=cluster_dir)
185 return ClusterDir(location=cluster_dir)
186
186
187 @classmethod
187 @classmethod
188 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
188 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
189 """Find an existing cluster dir by profile name, return its ClusterDir.
189 """Find an existing cluster dir by profile name, return its ClusterDir.
190
190
191 This searches through a sequence of paths for a cluster dir. If it
191 This searches through a sequence of paths for a cluster dir. If it
192 is not found, a :class:`ClusterDirError` exception will be raised.
192 is not found, a :class:`ClusterDirError` exception will be raised.
193
193
194 The search path algorithm is:
194 The search path algorithm is:
195 1. ``os.getcwd()``
195 1. ``os.getcwd()``
196 2. ``ipython_dir``
196 2. ``ipython_dir``
197 3. The directories found in the ":" separated
197 3. The directories found in the ":" separated
198 :env:`IPCLUSTER_DIR_PATH` environment variable.
198 :env:`IPCLUSTER_DIR_PATH` environment variable.
199
199
200 Parameters
200 Parameters
201 ----------
201 ----------
202 ipython_dir : unicode or str
202 ipython_dir : unicode or str
203 The IPython directory to use.
203 The IPython directory to use.
204 profile : unicode or str
204 profile : unicode or str
205 The name of the profile. The name of the cluster directory
205 The name of the profile. The name of the cluster directory
206 will be "cluster_<profile>".
206 will be "cluster_<profile>".
207 """
207 """
208 dirname = u'cluster_' + profile
208 dirname = u'cluster_' + profile
209 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
209 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
210 if cluster_dir_paths:
210 if cluster_dir_paths:
211 cluster_dir_paths = cluster_dir_paths.split(':')
211 cluster_dir_paths = cluster_dir_paths.split(':')
212 else:
212 else:
213 cluster_dir_paths = []
213 cluster_dir_paths = []
214 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
214 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
215 for p in paths:
215 for p in paths:
216 cluster_dir = os.path.join(p, dirname)
216 cluster_dir = os.path.join(p, dirname)
217 if os.path.isdir(cluster_dir):
217 if os.path.isdir(cluster_dir):
218 return ClusterDir(location=cluster_dir)
218 return ClusterDir(location=cluster_dir)
219 else:
219 else:
220 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
220 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
221
221
222 @classmethod
222 @classmethod
223 def find_cluster_dir(cls, cluster_dir):
223 def find_cluster_dir(cls, cluster_dir):
224 """Find/create a cluster dir and return its ClusterDir.
224 """Find/create a cluster dir and return its ClusterDir.
225
225
226 This will create the cluster directory if it doesn't exist.
226 This will create the cluster directory if it doesn't exist.
227
227
228 Parameters
228 Parameters
229 ----------
229 ----------
230 cluster_dir : unicode or str
230 cluster_dir : unicode or str
231 The path of the cluster directory. This is expanded using
231 The path of the cluster directory. This is expanded using
232 :func:`IPython.utils.genutils.expand_path`.
232 :func:`IPython.utils.genutils.expand_path`.
233 """
233 """
234 cluster_dir = expand_path(cluster_dir)
234 cluster_dir = expand_path(cluster_dir)
235 if not os.path.isdir(cluster_dir):
235 if not os.path.isdir(cluster_dir):
236 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
236 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
237 return ClusterDir(location=cluster_dir)
237 return ClusterDir(location=cluster_dir)
238
238
239
239
240 #-----------------------------------------------------------------------------
240 #-----------------------------------------------------------------------------
241 # Command line options
241 # Command line options
242 #-----------------------------------------------------------------------------
242 #-----------------------------------------------------------------------------
243
243
244 class ClusterDirConfigLoader(BaseAppConfigLoader):
244 class ClusterDirConfigLoader(BaseAppConfigLoader):
245
245
246 def _add_cluster_profile(self, parser):
246 def _add_cluster_profile(self, parser):
247 paa = parser.add_argument
247 paa = parser.add_argument
248 paa('-p', '--profile',
248 paa('-p', '--profile',
249 dest='Global.profile',type=unicode,
249 dest='Global.profile',type=unicode,
250 help=
250 help=
251 """The string name of the profile to be used. This determines the name
251 """The string name of the profile to be used. This determines the name
252 of the cluster dir as: cluster_<profile>. The default profile is named
252 of the cluster dir as: cluster_<profile>. The default profile is named
253 'default'. The cluster directory is resolve this way if the
253 'default'. The cluster directory is resolve this way if the
254 --cluster-dir option is not used.""",
254 --cluster-dir option is not used.""",
255 metavar='Global.profile')
255 metavar='Global.profile')
256
256
257 def _add_cluster_dir(self, parser):
257 def _add_cluster_dir(self, parser):
258 paa = parser.add_argument
258 paa = parser.add_argument
259 paa('--cluster-dir',
259 paa('--cluster-dir',
260 dest='Global.cluster_dir',type=unicode,
260 dest='Global.cluster_dir',type=unicode,
261 help="""Set the cluster dir. This overrides the logic used by the
261 help="""Set the cluster dir. This overrides the logic used by the
262 --profile option.""",
262 --profile option.""",
263 metavar='Global.cluster_dir')
263 metavar='Global.cluster_dir')
264
264
265 def _add_work_dir(self, parser):
265 def _add_work_dir(self, parser):
266 paa = parser.add_argument
266 paa = parser.add_argument
267 paa('--work-dir',
267 paa('--work-dir',
268 dest='Global.work_dir',type=unicode,
268 dest='Global.work_dir',type=unicode,
269 help='Set the working dir for the process.',
269 help='Set the working dir for the process.',
270 metavar='Global.work_dir')
270 metavar='Global.work_dir')
271
271
272 def _add_clean_logs(self, parser):
272 def _add_clean_logs(self, parser):
273 paa = parser.add_argument
273 paa = parser.add_argument
274 paa('--clean-logs',
274 paa('--clean-logs',
275 dest='Global.clean_logs', action='store_true',
275 dest='Global.clean_logs', action='store_true',
276 help='Delete old log flies before starting.')
276 help='Delete old log flies before starting.')
277
277
278 def _add_no_clean_logs(self, parser):
278 def _add_no_clean_logs(self, parser):
279 paa = parser.add_argument
279 paa = parser.add_argument
280 paa('--no-clean-logs',
280 paa('--no-clean-logs',
281 dest='Global.clean_logs', action='store_false',
281 dest='Global.clean_logs', action='store_false',
282 help="Don't Delete old log flies before starting.")
282 help="Don't Delete old log flies before starting.")
283
283
284 def _add_arguments(self):
284 def _add_arguments(self):
285 super(ClusterDirConfigLoader, self)._add_arguments()
285 super(ClusterDirConfigLoader, self)._add_arguments()
286 self._add_cluster_profile(self.parser)
286 self._add_cluster_profile(self.parser)
287 self._add_cluster_dir(self.parser)
287 self._add_cluster_dir(self.parser)
288 self._add_work_dir(self.parser)
288 self._add_work_dir(self.parser)
289 self._add_clean_logs(self.parser)
289 self._add_clean_logs(self.parser)
290 self._add_no_clean_logs(self.parser)
290 self._add_no_clean_logs(self.parser)
291
291
292
292
293 #-----------------------------------------------------------------------------
293 #-----------------------------------------------------------------------------
294 # Crash handler for this application
294 # Crash handler for this application
295 #-----------------------------------------------------------------------------
295 #-----------------------------------------------------------------------------
296
296
297
297
298 _message_template = """\
298 _message_template = """\
299 Oops, $self.app_name crashed. We do our best to make it stable, but...
299 Oops, $self.app_name crashed. We do our best to make it stable, but...
300
300
301 A crash report was automatically generated with the following information:
301 A crash report was automatically generated with the following information:
302 - A verbatim copy of the crash traceback.
302 - A verbatim copy of the crash traceback.
303 - Data on your current $self.app_name configuration.
303 - Data on your current $self.app_name configuration.
304
304
305 It was left in the file named:
305 It was left in the file named:
306 \t'$self.crash_report_fname'
306 \t'$self.crash_report_fname'
307 If you can email this file to the developers, the information in it will help
307 If you can email this file to the developers, the information in it will help
308 them in understanding and correcting the problem.
308 them in understanding and correcting the problem.
309
309
310 You can mail it to: $self.contact_name at $self.contact_email
310 You can mail it to: $self.contact_name at $self.contact_email
311 with the subject '$self.app_name Crash Report'.
311 with the subject '$self.app_name Crash Report'.
312
312
313 If you want to do it now, the following command will work (under Unix):
313 If you want to do it now, the following command will work (under Unix):
314 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
314 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
315
315
316 To ensure accurate tracking of this issue, please file a report about it at:
316 To ensure accurate tracking of this issue, please file a report about it at:
317 $self.bug_tracker
317 $self.bug_tracker
318 """
318 """
319
319
320 class ClusterDirCrashHandler(CrashHandler):
320 class ClusterDirCrashHandler(CrashHandler):
321 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
321 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
322
322
323 message_template = _message_template
323 message_template = _message_template
324
324
325 def __init__(self, app):
325 def __init__(self, app):
326 contact_name = release.authors['Brian'][0]
326 contact_name = release.authors['Brian'][0]
327 contact_email = release.authors['Brian'][1]
327 contact_email = release.authors['Brian'][1]
328 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
328 bug_tracker = 'http://github.com/ipython/ipython/issues'
329 super(ClusterDirCrashHandler,self).__init__(
329 super(ClusterDirCrashHandler,self).__init__(
330 app, contact_name, contact_email, bug_tracker
330 app, contact_name, contact_email, bug_tracker
331 )
331 )
332
332
333
333
334 #-----------------------------------------------------------------------------
334 #-----------------------------------------------------------------------------
335 # Main application
335 # Main application
336 #-----------------------------------------------------------------------------
336 #-----------------------------------------------------------------------------
337
337
338 class ApplicationWithClusterDir(Application):
338 class ApplicationWithClusterDir(Application):
339 """An application that puts everything into a cluster directory.
339 """An application that puts everything into a cluster directory.
340
340
341 Instead of looking for things in the ipython_dir, this type of application
341 Instead of looking for things in the ipython_dir, this type of application
342 will use its own private directory called the "cluster directory"
342 will use its own private directory called the "cluster directory"
343 for things like config files, log files, etc.
343 for things like config files, log files, etc.
344
344
345 The cluster directory is resolved as follows:
345 The cluster directory is resolved as follows:
346
346
347 * If the ``--cluster-dir`` option is given, it is used.
347 * If the ``--cluster-dir`` option is given, it is used.
348 * If ``--cluster-dir`` is not given, the application directory is
348 * If ``--cluster-dir`` is not given, the application directory is
349 resolve using the profile name as ``cluster_<profile>``. The search
349 resolve using the profile name as ``cluster_<profile>``. The search
350 path for this directory is then i) cwd if it is found there
350 path for this directory is then i) cwd if it is found there
351 and ii) in ipython_dir otherwise.
351 and ii) in ipython_dir otherwise.
352
352
353 The config file for the application is to be put in the cluster
353 The config file for the application is to be put in the cluster
354 dir and named the value of the ``config_file_name`` class attribute.
354 dir and named the value of the ``config_file_name`` class attribute.
355 """
355 """
356
356
357 command_line_loader = ClusterDirConfigLoader
357 command_line_loader = ClusterDirConfigLoader
358 crash_handler_class = ClusterDirCrashHandler
358 crash_handler_class = ClusterDirCrashHandler
359 auto_create_cluster_dir = True
359 auto_create_cluster_dir = True
360
360
361 def create_default_config(self):
361 def create_default_config(self):
362 super(ApplicationWithClusterDir, self).create_default_config()
362 super(ApplicationWithClusterDir, self).create_default_config()
363 self.default_config.Global.profile = u'default'
363 self.default_config.Global.profile = u'default'
364 self.default_config.Global.cluster_dir = u''
364 self.default_config.Global.cluster_dir = u''
365 self.default_config.Global.work_dir = os.getcwd()
365 self.default_config.Global.work_dir = os.getcwd()
366 self.default_config.Global.log_to_file = False
366 self.default_config.Global.log_to_file = False
367 self.default_config.Global.clean_logs = False
367 self.default_config.Global.clean_logs = False
368
368
369 def find_resources(self):
369 def find_resources(self):
370 """This resolves the cluster directory.
370 """This resolves the cluster directory.
371
371
372 This tries to find the cluster directory and if successful, it will
372 This tries to find the cluster directory and if successful, it will
373 have done:
373 have done:
374 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
374 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
375 the application.
375 the application.
376 * Sets ``self.cluster_dir`` attribute of the application and config
376 * Sets ``self.cluster_dir`` attribute of the application and config
377 objects.
377 objects.
378
378
379 The algorithm used for this is as follows:
379 The algorithm used for this is as follows:
380 1. Try ``Global.cluster_dir``.
380 1. Try ``Global.cluster_dir``.
381 2. Try using ``Global.profile``.
381 2. Try using ``Global.profile``.
382 3. If both of these fail and ``self.auto_create_cluster_dir`` is
382 3. If both of these fail and ``self.auto_create_cluster_dir`` is
383 ``True``, then create the new cluster dir in the IPython directory.
383 ``True``, then create the new cluster dir in the IPython directory.
384 4. If all fails, then raise :class:`ClusterDirError`.
384 4. If all fails, then raise :class:`ClusterDirError`.
385 """
385 """
386
386
387 try:
387 try:
388 cluster_dir = self.command_line_config.Global.cluster_dir
388 cluster_dir = self.command_line_config.Global.cluster_dir
389 except AttributeError:
389 except AttributeError:
390 cluster_dir = self.default_config.Global.cluster_dir
390 cluster_dir = self.default_config.Global.cluster_dir
391 cluster_dir = expand_path(cluster_dir)
391 cluster_dir = expand_path(cluster_dir)
392 try:
392 try:
393 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
393 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
394 except ClusterDirError:
394 except ClusterDirError:
395 pass
395 pass
396 else:
396 else:
397 self.log.info('Using existing cluster dir: %s' % \
397 self.log.info('Using existing cluster dir: %s' % \
398 self.cluster_dir_obj.location
398 self.cluster_dir_obj.location
399 )
399 )
400 self.finish_cluster_dir()
400 self.finish_cluster_dir()
401 return
401 return
402
402
403 try:
403 try:
404 self.profile = self.command_line_config.Global.profile
404 self.profile = self.command_line_config.Global.profile
405 except AttributeError:
405 except AttributeError:
406 self.profile = self.default_config.Global.profile
406 self.profile = self.default_config.Global.profile
407 try:
407 try:
408 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
408 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
409 self.ipython_dir, self.profile)
409 self.ipython_dir, self.profile)
410 except ClusterDirError:
410 except ClusterDirError:
411 pass
411 pass
412 else:
412 else:
413 self.log.info('Using existing cluster dir: %s' % \
413 self.log.info('Using existing cluster dir: %s' % \
414 self.cluster_dir_obj.location
414 self.cluster_dir_obj.location
415 )
415 )
416 self.finish_cluster_dir()
416 self.finish_cluster_dir()
417 return
417 return
418
418
419 if self.auto_create_cluster_dir:
419 if self.auto_create_cluster_dir:
420 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
420 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
421 self.ipython_dir, self.profile
421 self.ipython_dir, self.profile
422 )
422 )
423 self.log.info('Creating new cluster dir: %s' % \
423 self.log.info('Creating new cluster dir: %s' % \
424 self.cluster_dir_obj.location
424 self.cluster_dir_obj.location
425 )
425 )
426 self.finish_cluster_dir()
426 self.finish_cluster_dir()
427 else:
427 else:
428 raise ClusterDirError('Could not find a valid cluster directory.')
428 raise ClusterDirError('Could not find a valid cluster directory.')
429
429
430 def finish_cluster_dir(self):
430 def finish_cluster_dir(self):
431 # Set the cluster directory
431 # Set the cluster directory
432 self.cluster_dir = self.cluster_dir_obj.location
432 self.cluster_dir = self.cluster_dir_obj.location
433
433
434 # These have to be set because they could be different from the one
434 # These have to be set because they could be different from the one
435 # that we just computed. Because command line has the highest
435 # that we just computed. Because command line has the highest
436 # priority, this will always end up in the master_config.
436 # priority, this will always end up in the master_config.
437 self.default_config.Global.cluster_dir = self.cluster_dir
437 self.default_config.Global.cluster_dir = self.cluster_dir
438 self.command_line_config.Global.cluster_dir = self.cluster_dir
438 self.command_line_config.Global.cluster_dir = self.cluster_dir
439
439
440 def find_config_file_name(self):
440 def find_config_file_name(self):
441 """Find the config file name for this application."""
441 """Find the config file name for this application."""
442 # For this type of Application it should be set as a class attribute.
442 # For this type of Application it should be set as a class attribute.
443 if not hasattr(self, 'default_config_file_name'):
443 if not hasattr(self, 'default_config_file_name'):
444 self.log.critical("No config filename found")
444 self.log.critical("No config filename found")
445 else:
445 else:
446 self.config_file_name = self.default_config_file_name
446 self.config_file_name = self.default_config_file_name
447
447
448 def find_config_file_paths(self):
448 def find_config_file_paths(self):
449 # Set the search path to to the cluster directory. We should NOT
449 # Set the search path to to the cluster directory. We should NOT
450 # include IPython.config.default here as the default config files
450 # include IPython.config.default here as the default config files
451 # are ALWAYS automatically moved to the cluster directory.
451 # are ALWAYS automatically moved to the cluster directory.
452 conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default')
452 conf_dir = os.path.join(get_ipython_package_dir(), 'config', 'default')
453 self.config_file_paths = (self.cluster_dir,)
453 self.config_file_paths = (self.cluster_dir,)
454
454
455 def pre_construct(self):
455 def pre_construct(self):
456 # The log and security dirs were set earlier, but here we put them
456 # The log and security dirs were set earlier, but here we put them
457 # into the config and log them.
457 # into the config and log them.
458 config = self.master_config
458 config = self.master_config
459 sdir = self.cluster_dir_obj.security_dir
459 sdir = self.cluster_dir_obj.security_dir
460 self.security_dir = config.Global.security_dir = sdir
460 self.security_dir = config.Global.security_dir = sdir
461 ldir = self.cluster_dir_obj.log_dir
461 ldir = self.cluster_dir_obj.log_dir
462 self.log_dir = config.Global.log_dir = ldir
462 self.log_dir = config.Global.log_dir = ldir
463 pdir = self.cluster_dir_obj.pid_dir
463 pdir = self.cluster_dir_obj.pid_dir
464 self.pid_dir = config.Global.pid_dir = pdir
464 self.pid_dir = config.Global.pid_dir = pdir
465 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
465 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
466 config.Global.work_dir = unicode(expand_path(config.Global.work_dir))
466 config.Global.work_dir = unicode(expand_path(config.Global.work_dir))
467 # Change to the working directory. We do this just before construct
467 # Change to the working directory. We do this just before construct
468 # is called so all the components there have the right working dir.
468 # is called so all the components there have the right working dir.
469 self.to_work_dir()
469 self.to_work_dir()
470
470
471 def to_work_dir(self):
471 def to_work_dir(self):
472 wd = self.master_config.Global.work_dir
472 wd = self.master_config.Global.work_dir
473 if unicode(wd) != unicode(os.getcwd()):
473 if unicode(wd) != unicode(os.getcwd()):
474 os.chdir(wd)
474 os.chdir(wd)
475 self.log.info("Changing to working dir: %s" % wd)
475 self.log.info("Changing to working dir: %s" % wd)
476
476
477 def start_logging(self):
477 def start_logging(self):
478 # Remove old log files
478 # Remove old log files
479 if self.master_config.Global.clean_logs:
479 if self.master_config.Global.clean_logs:
480 log_dir = self.master_config.Global.log_dir
480 log_dir = self.master_config.Global.log_dir
481 for f in os.listdir(log_dir):
481 for f in os.listdir(log_dir):
482 if f.startswith(self.name + u'-') and f.endswith('.log'):
482 if f.startswith(self.name + u'-') and f.endswith('.log'):
483 os.remove(os.path.join(log_dir, f))
483 os.remove(os.path.join(log_dir, f))
484 # Start logging to the new log file
484 # Start logging to the new log file
485 if self.master_config.Global.log_to_file:
485 if self.master_config.Global.log_to_file:
486 log_filename = self.name + u'-' + str(os.getpid()) + u'.log'
486 log_filename = self.name + u'-' + str(os.getpid()) + u'.log'
487 logfile = os.path.join(self.log_dir, log_filename)
487 logfile = os.path.join(self.log_dir, log_filename)
488 open_log_file = open(logfile, 'w')
488 open_log_file = open(logfile, 'w')
489 else:
489 else:
490 open_log_file = sys.stdout
490 open_log_file = sys.stdout
491 log.startLogging(open_log_file)
491 log.startLogging(open_log_file)
492
492
493 def write_pid_file(self, overwrite=False):
493 def write_pid_file(self, overwrite=False):
494 """Create a .pid file in the pid_dir with my pid.
494 """Create a .pid file in the pid_dir with my pid.
495
495
496 This must be called after pre_construct, which sets `self.pid_dir`.
496 This must be called after pre_construct, which sets `self.pid_dir`.
497 This raises :exc:`PIDFileError` if the pid file exists already.
497 This raises :exc:`PIDFileError` if the pid file exists already.
498 """
498 """
499 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
499 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
500 if os.path.isfile(pid_file):
500 if os.path.isfile(pid_file):
501 pid = self.get_pid_from_file()
501 pid = self.get_pid_from_file()
502 if not overwrite:
502 if not overwrite:
503 raise PIDFileError(
503 raise PIDFileError(
504 'The pid file [%s] already exists. \nThis could mean that this '
504 'The pid file [%s] already exists. \nThis could mean that this '
505 'server is already running with [pid=%s].' % (pid_file, pid)
505 'server is already running with [pid=%s].' % (pid_file, pid)
506 )
506 )
507 with open(pid_file, 'w') as f:
507 with open(pid_file, 'w') as f:
508 self.log.info("Creating pid file: %s" % pid_file)
508 self.log.info("Creating pid file: %s" % pid_file)
509 f.write(repr(os.getpid())+'\n')
509 f.write(repr(os.getpid())+'\n')
510
510
511 def remove_pid_file(self):
511 def remove_pid_file(self):
512 """Remove the pid file.
512 """Remove the pid file.
513
513
514 This should be called at shutdown by registering a callback with
514 This should be called at shutdown by registering a callback with
515 :func:`reactor.addSystemEventTrigger`. This needs to return
515 :func:`reactor.addSystemEventTrigger`. This needs to return
516 ``None``.
516 ``None``.
517 """
517 """
518 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
518 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
519 if os.path.isfile(pid_file):
519 if os.path.isfile(pid_file):
520 try:
520 try:
521 self.log.info("Removing pid file: %s" % pid_file)
521 self.log.info("Removing pid file: %s" % pid_file)
522 os.remove(pid_file)
522 os.remove(pid_file)
523 except:
523 except:
524 self.log.warn("Error removing the pid file: %s" % pid_file)
524 self.log.warn("Error removing the pid file: %s" % pid_file)
525
525
526 def get_pid_from_file(self):
526 def get_pid_from_file(self):
527 """Get the pid from the pid file.
527 """Get the pid from the pid file.
528
528
529 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
529 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
530 """
530 """
531 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
531 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
532 if os.path.isfile(pid_file):
532 if os.path.isfile(pid_file):
533 with open(pid_file, 'r') as f:
533 with open(pid_file, 'r') as f:
534 pid = int(f.read().strip())
534 pid = int(f.read().strip())
535 return pid
535 return pid
536 else:
536 else:
537 raise PIDFileError('pid file not found: %s' % pid_file)
537 raise PIDFileError('pid file not found: %s' % pid_file)
538
538
General Comments 0
You need to be logged in to leave comments. Login now