##// END OF EJS Templates
Full refactor of ipstruct.Struct....
Brian Granger -
Show More
@@ -1,771 +1,771 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 IPython -- An enhanced Interactive Python
3 IPython -- An enhanced Interactive Python
4
4
5 Requires Python 2.1 or better.
5 Requires Python 2.1 or better.
6
6
7 This file contains the main make_IPython() starter function.
7 This file contains the main make_IPython() starter function.
8 """
8 """
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2008-2009 The IPython Development Team
11 # Copyright (C) 2008-2009 The IPython Development Team
12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
16 #*****************************************************************************
17
17
18 try:
18 try:
19 credits._Printer__data = """
19 credits._Printer__data = """
20 Python: %s
20 Python: %s
21
21
22 IPython: The IPython Development Team.
22 IPython: The IPython Development Team.
23 See http://ipython.scipy.org for more information.""" \
23 See http://ipython.scipy.org for more information.""" \
24 % credits._Printer__data
24 % credits._Printer__data
25
25
26 copyright._Printer__data += """
26 copyright._Printer__data += """
27
27
28 Copyright (c) 2008-2009 The IPython Development Team.
28 Copyright (c) 2008-2009 The IPython Development Team.
29 Copyright (c) 2001-2007 Fernando Perez, Janko Hauser, Nathan Gray.
29 Copyright (c) 2001-2007 Fernando Perez, Janko Hauser, Nathan Gray.
30 All Rights Reserved."""
30 All Rights Reserved."""
31 except NameError:
31 except NameError:
32 # Can happen if ipython was started with 'python -S', so that site.py is
32 # Can happen if ipython was started with 'python -S', so that site.py is
33 # not loaded
33 # not loaded
34 pass
34 pass
35
35
36 #****************************************************************************
36 #****************************************************************************
37 # Required modules
37 # Required modules
38
38
39 # From the standard library
39 # From the standard library
40 import __main__
40 import __main__
41 import __builtin__
41 import __builtin__
42 import os
42 import os
43 import sys
43 import sys
44 from pprint import pprint
44 from pprint import pprint
45
45
46 # Our own
46 # Our own
47 from IPython.utils import DPyGetOpt
47 from IPython.utils import DPyGetOpt
48 from IPython.core import release
48 from IPython.core import release
49 from IPython.utils.ipstruct import Struct
49 from IPython.utils.ipstruct import Struct
50 from IPython.core.outputtrap import OutputTrap
50 from IPython.core.outputtrap import OutputTrap
51 from IPython.config.configloader import ConfigLoader
51 from IPython.config.configloader import ConfigLoader
52 from IPython.core.iplib import InteractiveShell
52 from IPython.core.iplib import InteractiveShell
53 from IPython.core.usage import cmd_line_usage, interactive_usage
53 from IPython.core.usage import cmd_line_usage, interactive_usage
54 from IPython.utils.genutils import *
54 from IPython.utils.genutils import *
55
55
56 def force_import(modname,force_reload=False):
56 def force_import(modname,force_reload=False):
57 if modname in sys.modules and force_reload:
57 if modname in sys.modules and force_reload:
58 info("reloading: %s" % modname)
58 info("reloading: %s" % modname)
59 reload(sys.modules[modname])
59 reload(sys.modules[modname])
60 else:
60 else:
61 __import__(modname)
61 __import__(modname)
62
62
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,
65 def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,
66 rc_override=None,shell_class=InteractiveShell,
66 rc_override=None,shell_class=InteractiveShell,
67 embedded=False,**kw):
67 embedded=False,**kw):
68 """This is a dump of IPython into a single function.
68 """This is a dump of IPython into a single function.
69
69
70 Later it will have to be broken up in a sensible manner.
70 Later it will have to be broken up in a sensible manner.
71
71
72 Arguments:
72 Arguments:
73
73
74 - argv: a list similar to sys.argv[1:]. It should NOT contain the desired
74 - argv: a list similar to sys.argv[1:]. It should NOT contain the desired
75 script name, b/c DPyGetOpt strips the first argument only for the real
75 script name, b/c DPyGetOpt strips the first argument only for the real
76 sys.argv.
76 sys.argv.
77
77
78 - user_ns: a dict to be used as the user's namespace."""
78 - user_ns: a dict to be used as the user's namespace."""
79
79
80 #----------------------------------------------------------------------
80 #----------------------------------------------------------------------
81 # Defaults and initialization
81 # Defaults and initialization
82
82
83 # For developer debugging, deactivates crash handler and uses pdb.
83 # For developer debugging, deactivates crash handler and uses pdb.
84 DEVDEBUG = False
84 DEVDEBUG = False
85
85
86 if argv is None:
86 if argv is None:
87 argv = sys.argv
87 argv = sys.argv
88
88
89 # __IP is the main global that lives throughout and represents the whole
89 # __IP is the main global that lives throughout and represents the whole
90 # application. If the user redefines it, all bets are off as to what
90 # application. If the user redefines it, all bets are off as to what
91 # happens.
91 # happens.
92
92
93 # __IP is the name of he global which the caller will have accessible as
93 # __IP is the name of he global which the caller will have accessible as
94 # __IP.name. We set its name via the first parameter passed to
94 # __IP.name. We set its name via the first parameter passed to
95 # InteractiveShell:
95 # InteractiveShell:
96
96
97 IP = shell_class('__IP',user_ns=user_ns,user_global_ns=user_global_ns,
97 IP = shell_class('__IP',user_ns=user_ns,user_global_ns=user_global_ns,
98 embedded=embedded,**kw)
98 embedded=embedded,**kw)
99
99
100 # Put 'help' in the user namespace
100 # Put 'help' in the user namespace
101 try:
101 try:
102 from site import _Helper
102 from site import _Helper
103 IP.user_ns['help'] = _Helper()
103 IP.user_ns['help'] = _Helper()
104 except ImportError:
104 except ImportError:
105 warn('help() not available - check site.py')
105 warn('help() not available - check site.py')
106
106
107 if DEVDEBUG:
107 if DEVDEBUG:
108 # For developer debugging only (global flag)
108 # For developer debugging only (global flag)
109 from IPython.core import ultratb
109 from IPython.core import ultratb
110 sys.excepthook = ultratb.VerboseTB(call_pdb=1)
110 sys.excepthook = ultratb.VerboseTB(call_pdb=1)
111
111
112 IP.BANNER_PARTS = ['Python %s\n'
112 IP.BANNER_PARTS = ['Python %s\n'
113 'Type "copyright", "credits" or "license" '
113 'Type "copyright", "credits" or "license" '
114 'for more information.\n'
114 'for more information.\n'
115 % (sys.version.split('\n')[0],),
115 % (sys.version.split('\n')[0],),
116 "IPython %s -- An enhanced Interactive Python."
116 "IPython %s -- An enhanced Interactive Python."
117 % (release.version,),
117 % (release.version,),
118 """\
118 """\
119 ? -> Introduction and overview of IPython's features.
119 ? -> Introduction and overview of IPython's features.
120 %quickref -> Quick reference.
120 %quickref -> Quick reference.
121 help -> Python's own help system.
121 help -> Python's own help system.
122 object? -> Details about 'object'. ?object also works, ?? prints more.
122 object? -> Details about 'object'. ?object also works, ?? prints more.
123 """ ]
123 """ ]
124
124
125 IP.usage = interactive_usage
125 IP.usage = interactive_usage
126
126
127 # Platform-dependent suffix.
127 # Platform-dependent suffix.
128 if os.name == 'posix':
128 if os.name == 'posix':
129 rc_suffix = ''
129 rc_suffix = ''
130 else:
130 else:
131 rc_suffix = '.ini'
131 rc_suffix = '.ini'
132
132
133 # default directory for configuration
133 # default directory for configuration
134 ipythondir_def = get_ipython_dir()
134 ipythondir_def = get_ipython_dir()
135
135
136 sys.path.insert(0, '') # add . to sys.path. Fix from Prabhu Ramachandran
136 sys.path.insert(0, '') # add . to sys.path. Fix from Prabhu Ramachandran
137
137
138 # we need the directory where IPython itself is installed
138 # we need the directory where IPython itself is installed
139 import IPython
139 import IPython
140 IPython_dir = os.path.dirname(IPython.__file__)
140 IPython_dir = os.path.dirname(IPython.__file__)
141 del IPython
141 del IPython
142
142
143 #-------------------------------------------------------------------------
143 #-------------------------------------------------------------------------
144 # Command line handling
144 # Command line handling
145
145
146 # Valid command line options (uses DPyGetOpt syntax, like Perl's
146 # Valid command line options (uses DPyGetOpt syntax, like Perl's
147 # GetOpt::Long)
147 # GetOpt::Long)
148
148
149 # Any key not listed here gets deleted even if in the file (like session
149 # Any key not listed here gets deleted even if in the file (like session
150 # or profile). That's deliberate, to maintain the rc namespace clean.
150 # or profile). That's deliberate, to maintain the rc namespace clean.
151
151
152 # Each set of options appears twice: under _conv only the names are
152 # Each set of options appears twice: under _conv only the names are
153 # listed, indicating which type they must be converted to when reading the
153 # listed, indicating which type they must be converted to when reading the
154 # ipythonrc file. And under DPyGetOpt they are listed with the regular
154 # ipythonrc file. And under DPyGetOpt they are listed with the regular
155 # DPyGetOpt syntax (=s,=i,:f,etc).
155 # DPyGetOpt syntax (=s,=i,:f,etc).
156
156
157 # Make sure there's a space before each end of line (they get auto-joined!)
157 # Make sure there's a space before each end of line (they get auto-joined!)
158 cmdline_opts = ('autocall=i autoindent! automagic! banner! cache_size|cs=i '
158 cmdline_opts = ('autocall=i autoindent! automagic! banner! cache_size|cs=i '
159 'c=s classic|cl color_info! colors=s confirm_exit! '
159 'c=s classic|cl color_info! colors=s confirm_exit! '
160 'debug! deep_reload! editor=s log|l messages! nosep '
160 'debug! deep_reload! editor=s log|l messages! nosep '
161 'object_info_string_level=i pdb! '
161 'object_info_string_level=i pdb! '
162 'pprint! prompt_in1|pi1=s prompt_in2|pi2=s prompt_out|po=s '
162 'pprint! prompt_in1|pi1=s prompt_in2|pi2=s prompt_out|po=s '
163 'pydb! '
163 'pydb! '
164 'pylab_import_all! '
164 'pylab_import_all! '
165 'quick screen_length|sl=i prompts_pad_left=i '
165 'quick screen_length|sl=i prompts_pad_left=i '
166 'logfile|lf=s logplay|lp=s profile|p=s '
166 'logfile|lf=s logplay|lp=s profile|p=s '
167 'readline! readline_merge_completions! '
167 'readline! readline_merge_completions! '
168 'readline_omit__names! '
168 'readline_omit__names! '
169 'rcfile=s separate_in|si=s separate_out|so=s '
169 'rcfile=s separate_in|si=s separate_out|so=s '
170 'separate_out2|so2=s xmode=s wildcards_case_sensitive! '
170 'separate_out2|so2=s xmode=s wildcards_case_sensitive! '
171 'magic_docstrings system_verbose! '
171 'magic_docstrings system_verbose! '
172 'multi_line_specials! '
172 'multi_line_specials! '
173 'term_title! wxversion=s '
173 'term_title! wxversion=s '
174 'autoedit_syntax!')
174 'autoedit_syntax!')
175
175
176 # Options that can *only* appear at the cmd line (not in rcfiles).
176 # Options that can *only* appear at the cmd line (not in rcfiles).
177
177
178 cmdline_only = ('help interact|i ipythondir=s Version upgrade '
178 cmdline_only = ('help interact|i ipythondir=s Version upgrade '
179 'gthread! qthread! q4thread! wthread! tkthread! pylab! tk! '
179 'gthread! qthread! q4thread! wthread! tkthread! pylab! tk! '
180 # 'twisted!' # disabled for now.
180 # 'twisted!' # disabled for now.
181 )
181 )
182
182
183 # Build the actual name list to be used by DPyGetOpt
183 # Build the actual name list to be used by DPyGetOpt
184 opts_names = qw(cmdline_opts) + qw(cmdline_only)
184 opts_names = qw(cmdline_opts) + qw(cmdline_only)
185
185
186 # Set sensible command line defaults.
186 # Set sensible command line defaults.
187 # This should have everything from cmdline_opts and cmdline_only
187 # This should have everything from cmdline_opts and cmdline_only
188 opts_def = Struct(autocall = 1,
188 opts_def = Struct(autocall = 1,
189 autoedit_syntax = 0,
189 autoedit_syntax = 0,
190 autoindent = 0,
190 autoindent = 0,
191 automagic = 1,
191 automagic = 1,
192 autoexec = [],
192 autoexec = [],
193 banner = 1,
193 banner = 1,
194 c = '',
194 c = '',
195 cache_size = 1000,
195 cache_size = 1000,
196 classic = 0,
196 classic = 0,
197 color_info = 0,
197 color_info = 0,
198 colors = 'NoColor',
198 colors = 'NoColor',
199 confirm_exit = 1,
199 confirm_exit = 1,
200 debug = 0,
200 debug = 0,
201 deep_reload = 0,
201 deep_reload = 0,
202 editor = '0',
202 editor = '0',
203 gthread = 0,
203 gthread = 0,
204 help = 0,
204 help = 0,
205 interact = 0,
205 interact = 0,
206 ipythondir = ipythondir_def,
206 ipythondir = ipythondir_def,
207 log = 0,
207 log = 0,
208 logfile = '',
208 logfile = '',
209 logplay = '',
209 logplay = '',
210 messages = 1,
210 messages = 1,
211 multi_line_specials = 1,
211 multi_line_specials = 1,
212 nosep = 0,
212 nosep = 0,
213 object_info_string_level = 0,
213 object_info_string_level = 0,
214 pdb = 0,
214 pdb = 0,
215 pprint = 0,
215 pprint = 0,
216 profile = '',
216 profile = '',
217 prompt_in1 = 'In [\\#]: ',
217 prompt_in1 = 'In [\\#]: ',
218 prompt_in2 = ' .\\D.: ',
218 prompt_in2 = ' .\\D.: ',
219 prompt_out = 'Out[\\#]: ',
219 prompt_out = 'Out[\\#]: ',
220 prompts_pad_left = 1,
220 prompts_pad_left = 1,
221 pydb = 0,
221 pydb = 0,
222 pylab = 0,
222 pylab = 0,
223 pylab_import_all = 1,
223 pylab_import_all = 1,
224 q4thread = 0,
224 q4thread = 0,
225 qthread = 0,
225 qthread = 0,
226 quick = 0,
226 quick = 0,
227 quiet = 0,
227 quiet = 0,
228 rcfile = 'ipythonrc' + rc_suffix,
228 rcfile = 'ipythonrc' + rc_suffix,
229 readline = 1,
229 readline = 1,
230 readline_merge_completions = 1,
230 readline_merge_completions = 1,
231 readline_omit__names = 0,
231 readline_omit__names = 0,
232 screen_length = 0,
232 screen_length = 0,
233 separate_in = '\n',
233 separate_in = '\n',
234 separate_out = '\n',
234 separate_out = '\n',
235 separate_out2 = '',
235 separate_out2 = '',
236 system_header = 'IPython system call: ',
236 system_header = 'IPython system call: ',
237 system_verbose = 0,
237 system_verbose = 0,
238 term_title = 1,
238 term_title = 1,
239 tk = 0,
239 tk = 0,
240 #twisted= 0, # disabled for now
240 #twisted= 0, # disabled for now
241 upgrade = 0,
241 upgrade = 0,
242 Version = 0,
242 Version = 0,
243 wildcards_case_sensitive = 1,
243 wildcards_case_sensitive = 1,
244 wthread = 0,
244 wthread = 0,
245 wxversion = '0',
245 wxversion = '0',
246 xmode = 'Context',
246 xmode = 'Context',
247 magic_docstrings = 0, # undocumented, for doc generation
247 magic_docstrings = 0, # undocumented, for doc generation
248 )
248 )
249
249
250 # Things that will *only* appear in rcfiles (not at the command line).
250 # Things that will *only* appear in rcfiles (not at the command line).
251 # Make sure there's a space before each end of line (they get auto-joined!)
251 # Make sure there's a space before each end of line (they get auto-joined!)
252 rcfile_opts = { qwflat: 'include import_mod import_all execfile ',
252 rcfile_opts = { qwflat: 'include import_mod import_all execfile ',
253 qw_lol: 'import_some ',
253 qw_lol: 'import_some ',
254 # for things with embedded whitespace:
254 # for things with embedded whitespace:
255 list_strings:'execute alias readline_parse_and_bind ',
255 list_strings:'execute alias readline_parse_and_bind ',
256 # Regular strings need no conversion:
256 # Regular strings need no conversion:
257 None:'readline_remove_delims ',
257 None:'readline_remove_delims ',
258 }
258 }
259 # Default values for these
259 # Default values for these
260 rc_def = Struct(include = [],
260 rc_def = Struct(include = [],
261 import_mod = [],
261 import_mod = [],
262 import_all = [],
262 import_all = [],
263 import_some = [[]],
263 import_some = [[]],
264 execute = [],
264 execute = [],
265 execfile = [],
265 execfile = [],
266 alias = [],
266 alias = [],
267 readline_parse_and_bind = [],
267 readline_parse_and_bind = [],
268 readline_remove_delims = '',
268 readline_remove_delims = '',
269 )
269 )
270
270
271 # Build the type conversion dictionary from the above tables:
271 # Build the type conversion dictionary from the above tables:
272 typeconv = rcfile_opts.copy()
272 typeconv = rcfile_opts.copy()
273 typeconv.update(optstr2types(cmdline_opts))
273 typeconv.update(optstr2types(cmdline_opts))
274
274
275 # FIXME: the None key appears in both, put that back together by hand. Ugly!
275 # FIXME: the None key appears in both, put that back together by hand. Ugly!
276 typeconv[None] += ' ' + rcfile_opts[None]
276 typeconv[None] += ' ' + rcfile_opts[None]
277
277
278 # Remove quotes at ends of all strings (used to protect spaces)
278 # Remove quotes at ends of all strings (used to protect spaces)
279 typeconv[unquote_ends] = typeconv[None]
279 typeconv[unquote_ends] = typeconv[None]
280 del typeconv[None]
280 del typeconv[None]
281
281
282 # Build the list we'll use to make all config decisions with defaults:
282 # Build the list we'll use to make all config decisions with defaults:
283 opts_all = opts_def.copy()
283 opts_all = opts_def.copy()
284 opts_all.update(rc_def)
284 opts_all.update(rc_def)
285
285
286 # Build conflict resolver for recursive loading of config files:
286 # Build conflict resolver for recursive loading of config files:
287 # - preserve means the outermost file maintains the value, it is not
287 # - preserve means the outermost file maintains the value, it is not
288 # overwritten if an included file has the same key.
288 # overwritten if an included file has the same key.
289 # - add_flip applies + to the two values, so it better make sense to add
289 # - add_flip applies + to the two values, so it better make sense to add
290 # those types of keys. But it flips them first so that things loaded
290 # those types of keys. But it flips them first so that things loaded
291 # deeper in the inclusion chain have lower precedence.
291 # deeper in the inclusion chain have lower precedence.
292 conflict = {'preserve': ' '.join([ typeconv[int],
292 conflict = {'preserve': ' '.join([ typeconv[int],
293 typeconv[unquote_ends] ]),
293 typeconv[unquote_ends] ]),
294 'add_flip': ' '.join([ typeconv[qwflat],
294 'add_flip': ' '.join([ typeconv[qwflat],
295 typeconv[qw_lol],
295 typeconv[qw_lol],
296 typeconv[list_strings] ])
296 typeconv[list_strings] ])
297 }
297 }
298
298
299 # Now actually process the command line
299 # Now actually process the command line
300 getopt = DPyGetOpt.DPyGetOpt()
300 getopt = DPyGetOpt.DPyGetOpt()
301 getopt.setIgnoreCase(0)
301 getopt.setIgnoreCase(0)
302
302
303 getopt.parseConfiguration(opts_names)
303 getopt.parseConfiguration(opts_names)
304
304
305 try:
305 try:
306 getopt.processArguments(argv)
306 getopt.processArguments(argv)
307 except DPyGetOpt.ArgumentError, exc:
307 except DPyGetOpt.ArgumentError, exc:
308 print cmd_line_usage
308 print cmd_line_usage
309 warn('\nError in Arguments: "%s"' % exc)
309 warn('\nError in Arguments: "%s"' % exc)
310 sys.exit(1)
310 sys.exit(1)
311
311
312 # convert the options dict to a struct for much lighter syntax later
312 # convert the options dict to a struct for much lighter syntax later
313 opts = Struct(getopt.optionValues)
313 opts = Struct(getopt.optionValues)
314 args = getopt.freeValues
314 args = getopt.freeValues
315
315
316 # this is the struct (which has default values at this point) with which
316 # this is the struct (which has default values at this point) with which
317 # we make all decisions:
317 # we make all decisions:
318 opts_all.update(opts)
318 opts_all.update(opts)
319
319
320 # Options that force an immediate exit
320 # Options that force an immediate exit
321 if opts_all.help:
321 if opts_all.help:
322 page(cmd_line_usage)
322 page(cmd_line_usage)
323 sys.exit()
323 sys.exit()
324
324
325 if opts_all.Version:
325 if opts_all.Version:
326 print release.version
326 print release.version
327 sys.exit()
327 sys.exit()
328
328
329 if opts_all.magic_docstrings:
329 if opts_all.magic_docstrings:
330 IP.magic_magic('-latex')
330 IP.magic_magic('-latex')
331 sys.exit()
331 sys.exit()
332
332
333 # add personal ipythondir to sys.path so that users can put things in
333 # add personal ipythondir to sys.path so that users can put things in
334 # there for customization
334 # there for customization
335 sys.path.append(os.path.abspath(opts_all.ipythondir))
335 sys.path.append(os.path.abspath(opts_all.ipythondir))
336
336
337 # Create user config directory if it doesn't exist. This must be done
337 # Create user config directory if it doesn't exist. This must be done
338 # *after* getting the cmd line options.
338 # *after* getting the cmd line options.
339 if not os.path.isdir(opts_all.ipythondir):
339 if not os.path.isdir(opts_all.ipythondir):
340 IP.user_setup(opts_all.ipythondir,rc_suffix,'install')
340 IP.user_setup(opts_all.ipythondir,rc_suffix,'install')
341
341
342 # upgrade user config files while preserving a copy of the originals
342 # upgrade user config files while preserving a copy of the originals
343 if opts_all.upgrade:
343 if opts_all.upgrade:
344 IP.user_setup(opts_all.ipythondir,rc_suffix,'upgrade')
344 IP.user_setup(opts_all.ipythondir,rc_suffix,'upgrade')
345
345
346 # check mutually exclusive options in the *original* command line
346 # check mutually exclusive options in the *original* command line
347 mutex_opts(opts,[qw('log logfile'),qw('rcfile profile'),
347 mutex_opts(opts,[qw('log logfile'),qw('rcfile profile'),
348 qw('classic profile'),qw('classic rcfile')])
348 qw('classic profile'),qw('classic rcfile')])
349
349
350 #---------------------------------------------------------------------------
350 #---------------------------------------------------------------------------
351 # Log replay
351 # Log replay
352
352
353 # if -logplay, we need to 'become' the other session. That basically means
353 # if -logplay, we need to 'become' the other session. That basically means
354 # replacing the current command line environment with that of the old
354 # replacing the current command line environment with that of the old
355 # session and moving on.
355 # session and moving on.
356
356
357 # this is needed so that later we know we're in session reload mode, as
357 # this is needed so that later we know we're in session reload mode, as
358 # opts_all will get overwritten:
358 # opts_all will get overwritten:
359 load_logplay = 0
359 load_logplay = 0
360
360
361 if opts_all.logplay:
361 if opts_all.logplay:
362 load_logplay = opts_all.logplay
362 load_logplay = opts_all.logplay
363 opts_debug_save = opts_all.debug
363 opts_debug_save = opts_all.debug
364 try:
364 try:
365 logplay = open(opts_all.logplay)
365 logplay = open(opts_all.logplay)
366 except IOError:
366 except IOError:
367 if opts_all.debug: IP.InteractiveTB()
367 if opts_all.debug: IP.InteractiveTB()
368 warn('Could not open logplay file '+`opts_all.logplay`)
368 warn('Could not open logplay file '+`opts_all.logplay`)
369 # restore state as if nothing had happened and move on, but make
369 # restore state as if nothing had happened and move on, but make
370 # sure that later we don't try to actually load the session file
370 # sure that later we don't try to actually load the session file
371 logplay = None
371 logplay = None
372 load_logplay = 0
372 load_logplay = 0
373 del opts_all.logplay
373 del opts_all.logplay
374 else:
374 else:
375 try:
375 try:
376 logplay.readline()
376 logplay.readline()
377 logplay.readline();
377 logplay.readline();
378 # this reloads that session's command line
378 # this reloads that session's command line
379 cmd = logplay.readline()[6:]
379 cmd = logplay.readline()[6:]
380 exec cmd
380 exec cmd
381 # restore the true debug flag given so that the process of
381 # restore the true debug flag given so that the process of
382 # session loading itself can be monitored.
382 # session loading itself can be monitored.
383 opts.debug = opts_debug_save
383 opts.debug = opts_debug_save
384 # save the logplay flag so later we don't overwrite the log
384 # save the logplay flag so later we don't overwrite the log
385 opts.logplay = load_logplay
385 opts.logplay = load_logplay
386 # now we must update our own structure with defaults
386 # now we must update our own structure with defaults
387 opts_all.update(opts)
387 opts_all.update(opts)
388 # now load args
388 # now load args
389 cmd = logplay.readline()[6:]
389 cmd = logplay.readline()[6:]
390 exec cmd
390 exec cmd
391 logplay.close()
391 logplay.close()
392 except:
392 except:
393 logplay.close()
393 logplay.close()
394 if opts_all.debug: IP.InteractiveTB()
394 if opts_all.debug: IP.InteractiveTB()
395 warn("Logplay file lacking full configuration information.\n"
395 warn("Logplay file lacking full configuration information.\n"
396 "I'll try to read it, but some things may not work.")
396 "I'll try to read it, but some things may not work.")
397
397
398 #-------------------------------------------------------------------------
398 #-------------------------------------------------------------------------
399 # set up output traps: catch all output from files, being run, modules
399 # set up output traps: catch all output from files, being run, modules
400 # loaded, etc. Then give it to the user in a clean form at the end.
400 # loaded, etc. Then give it to the user in a clean form at the end.
401
401
402 msg_out = 'Output messages. '
402 msg_out = 'Output messages. '
403 msg_err = 'Error messages. '
403 msg_err = 'Error messages. '
404 msg_sep = '\n'
404 msg_sep = '\n'
405 msg = Struct(config = OutputTrap('Configuration Loader',msg_out,
405 msg = Struct(config = OutputTrap('Configuration Loader',msg_out,
406 msg_err,msg_sep,debug,
406 msg_err,msg_sep,debug,
407 quiet_out=1),
407 quiet_out=1),
408 user_exec = OutputTrap('User File Execution',msg_out,
408 user_exec = OutputTrap('User File Execution',msg_out,
409 msg_err,msg_sep,debug),
409 msg_err,msg_sep,debug),
410 logplay = OutputTrap('Log Loader',msg_out,
410 logplay = OutputTrap('Log Loader',msg_out,
411 msg_err,msg_sep,debug),
411 msg_err,msg_sep,debug),
412 summary = ''
412 summary = ''
413 )
413 )
414
414
415 #-------------------------------------------------------------------------
415 #-------------------------------------------------------------------------
416 # Process user ipythonrc-type configuration files
416 # Process user ipythonrc-type configuration files
417
417
418 # turn on output trapping and log to msg.config
418 # turn on output trapping and log to msg.config
419 # remember that with debug on, trapping is actually disabled
419 # remember that with debug on, trapping is actually disabled
420 msg.config.trap_all()
420 msg.config.trap_all()
421
421
422 # look for rcfile in current or default directory
422 # look for rcfile in current or default directory
423 try:
423 try:
424 opts_all.rcfile = filefind(opts_all.rcfile,opts_all.ipythondir)
424 opts_all.rcfile = filefind(opts_all.rcfile,opts_all.ipythondir)
425 except IOError:
425 except IOError:
426 if opts_all.debug: IP.InteractiveTB()
426 if opts_all.debug: IP.InteractiveTB()
427 warn('Configuration file %s not found. Ignoring request.'
427 warn('Configuration file %s not found. Ignoring request.'
428 % (opts_all.rcfile) )
428 % (opts_all.rcfile) )
429
429
430 # 'profiles' are a shorthand notation for config filenames
430 # 'profiles' are a shorthand notation for config filenames
431 profile_handled_by_legacy = False
431 profile_handled_by_legacy = False
432 if opts_all.profile:
432 if opts_all.profile:
433
433
434 try:
434 try:
435 opts_all.rcfile = filefind('ipythonrc-' + opts_all.profile
435 opts_all.rcfile = filefind('ipythonrc-' + opts_all.profile
436 + rc_suffix,
436 + rc_suffix,
437 opts_all.ipythondir)
437 opts_all.ipythondir)
438 profile_handled_by_legacy = True
438 profile_handled_by_legacy = True
439 except IOError:
439 except IOError:
440 if opts_all.debug: IP.InteractiveTB()
440 if opts_all.debug: IP.InteractiveTB()
441 opts.profile = '' # remove profile from options if invalid
441 opts.profile = '' # remove profile from options if invalid
442 # We won't warn anymore, primary method is ipy_profile_PROFNAME
442 # We won't warn anymore, primary method is ipy_profile_PROFNAME
443 # which does trigger a warning.
443 # which does trigger a warning.
444
444
445 # load the config file
445 # load the config file
446 rcfiledata = None
446 rcfiledata = None
447 if opts_all.quick:
447 if opts_all.quick:
448 print 'Launching IPython in quick mode. No config file read.'
448 print 'Launching IPython in quick mode. No config file read.'
449 elif opts_all.rcfile:
449 elif opts_all.rcfile:
450 try:
450 try:
451 cfg_loader = ConfigLoader(conflict)
451 cfg_loader = ConfigLoader(conflict)
452 rcfiledata = cfg_loader.load(opts_all.rcfile,typeconv,
452 rcfiledata = cfg_loader.load(opts_all.rcfile,typeconv,
453 'include',opts_all.ipythondir,
453 'include',opts_all.ipythondir,
454 purge = 1,
454 purge = 1,
455 unique = conflict['preserve'])
455 unique = conflict['preserve'])
456 except:
456 except:
457 IP.InteractiveTB()
457 IP.InteractiveTB()
458 warn('Problems loading configuration file '+
458 warn('Problems loading configuration file '+
459 `opts_all.rcfile`+
459 `opts_all.rcfile`+
460 '\nStarting with default -bare bones- configuration.')
460 '\nStarting with default -bare bones- configuration.')
461 else:
461 else:
462 warn('No valid configuration file found in either currrent directory\n'+
462 warn('No valid configuration file found in either currrent directory\n'+
463 'or in the IPython config. directory: '+`opts_all.ipythondir`+
463 'or in the IPython config. directory: '+`opts_all.ipythondir`+
464 '\nProceeding with internal defaults.')
464 '\nProceeding with internal defaults.')
465
465
466 #------------------------------------------------------------------------
466 #------------------------------------------------------------------------
467 # Set exception handlers in mode requested by user.
467 # Set exception handlers in mode requested by user.
468 otrap = OutputTrap(trap_out=1) # trap messages from magic_xmode
468 otrap = OutputTrap(trap_out=1) # trap messages from magic_xmode
469 IP.magic_xmode(opts_all.xmode)
469 IP.magic_xmode(opts_all.xmode)
470 otrap.release_out()
470 otrap.release_out()
471
471
472 #------------------------------------------------------------------------
472 #------------------------------------------------------------------------
473 # Execute user config
473 # Execute user config
474
474
475 # Create a valid config structure with the right precedence order:
475 # Create a valid config structure with the right precedence order:
476 # defaults < rcfile < command line. This needs to be in the instance, so
476 # defaults < rcfile < command line. This needs to be in the instance, so
477 # that method calls below that rely on it find it.
477 # that method calls below that rely on it find it.
478 IP.rc = rc_def.copy()
478 IP.rc = rc_def.copy()
479
479
480 # Work with a local alias inside this routine to avoid unnecessary
480 # Work with a local alias inside this routine to avoid unnecessary
481 # attribute lookups.
481 # attribute lookups.
482 IP_rc = IP.rc
482 IP_rc = IP.rc
483
483
484 IP_rc.update(opts_def)
484 IP_rc.update(opts_def)
485 if rcfiledata:
485 if rcfiledata:
486 # now we can update
487 IP_rc.update(rcfiledata)
486 IP_rc.update(rcfiledata)
488 IP_rc.update(opts)
487 IP_rc.update(opts)
489 IP_rc.update(rc_override)
488 if rc_override is not None:
489 IP_rc.update(rc_override)
490
490
491 # Store the original cmd line for reference:
491 # Store the original cmd line for reference:
492 IP_rc.opts = opts
492 IP_rc.opts = opts
493 IP_rc.args = args
493 IP_rc.args = args
494
494
495 # create a *runtime* Struct like rc for holding parameters which may be
495 # create a *runtime* Struct like rc for holding parameters which may be
496 # created and/or modified by runtime user extensions.
496 # created and/or modified by runtime user extensions.
497 IP.runtime_rc = Struct()
497 IP.runtime_rc = Struct()
498
498
499 # from this point on, all config should be handled through IP_rc,
499 # from this point on, all config should be handled through IP_rc,
500 # opts* shouldn't be used anymore.
500 # opts* shouldn't be used anymore.
501
501
502
502
503 # update IP_rc with some special things that need manual
503 # update IP_rc with some special things that need manual
504 # tweaks. Basically options which affect other options. I guess this
504 # tweaks. Basically options which affect other options. I guess this
505 # should just be written so that options are fully orthogonal and we
505 # should just be written so that options are fully orthogonal and we
506 # wouldn't worry about this stuff!
506 # wouldn't worry about this stuff!
507
507
508 if IP_rc.classic:
508 if IP_rc.classic:
509 IP_rc.quick = 1
509 IP_rc.quick = 1
510 IP_rc.cache_size = 0
510 IP_rc.cache_size = 0
511 IP_rc.pprint = 0
511 IP_rc.pprint = 0
512 IP_rc.prompt_in1 = '>>> '
512 IP_rc.prompt_in1 = '>>> '
513 IP_rc.prompt_in2 = '... '
513 IP_rc.prompt_in2 = '... '
514 IP_rc.prompt_out = ''
514 IP_rc.prompt_out = ''
515 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
515 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
516 IP_rc.colors = 'NoColor'
516 IP_rc.colors = 'NoColor'
517 IP_rc.xmode = 'Plain'
517 IP_rc.xmode = 'Plain'
518
518
519 IP.pre_config_initialization()
519 IP.pre_config_initialization()
520 # configure readline
520 # configure readline
521
521
522 # update exception handlers with rc file status
522 # update exception handlers with rc file status
523 otrap.trap_out() # I don't want these messages ever.
523 otrap.trap_out() # I don't want these messages ever.
524 IP.magic_xmode(IP_rc.xmode)
524 IP.magic_xmode(IP_rc.xmode)
525 otrap.release_out()
525 otrap.release_out()
526
526
527 # activate logging if requested and not reloading a log
527 # activate logging if requested and not reloading a log
528 if IP_rc.logplay:
528 if IP_rc.logplay:
529 IP.magic_logstart(IP_rc.logplay + ' append')
529 IP.magic_logstart(IP_rc.logplay + ' append')
530 elif IP_rc.logfile:
530 elif IP_rc.logfile:
531 IP.magic_logstart(IP_rc.logfile)
531 IP.magic_logstart(IP_rc.logfile)
532 elif IP_rc.log:
532 elif IP_rc.log:
533 IP.magic_logstart()
533 IP.magic_logstart()
534
534
535 # find user editor so that it we don't have to look it up constantly
535 # find user editor so that it we don't have to look it up constantly
536 if IP_rc.editor.strip()=='0':
536 if IP_rc.editor.strip()=='0':
537 try:
537 try:
538 ed = os.environ['EDITOR']
538 ed = os.environ['EDITOR']
539 except KeyError:
539 except KeyError:
540 if os.name == 'posix':
540 if os.name == 'posix':
541 ed = 'vi' # the only one guaranteed to be there!
541 ed = 'vi' # the only one guaranteed to be there!
542 else:
542 else:
543 ed = 'notepad' # same in Windows!
543 ed = 'notepad' # same in Windows!
544 IP_rc.editor = ed
544 IP_rc.editor = ed
545
545
546 # Keep track of whether this is an embedded instance or not (useful for
546 # Keep track of whether this is an embedded instance or not (useful for
547 # post-mortems).
547 # post-mortems).
548 IP_rc.embedded = IP.embedded
548 IP_rc.embedded = IP.embedded
549
549
550 # Recursive reload
550 # Recursive reload
551 try:
551 try:
552 from IPython.lib import deepreload
552 from IPython.lib import deepreload
553 if IP_rc.deep_reload:
553 if IP_rc.deep_reload:
554 __builtin__.reload = deepreload.reload
554 __builtin__.reload = deepreload.reload
555 else:
555 else:
556 __builtin__.dreload = deepreload.reload
556 __builtin__.dreload = deepreload.reload
557 del deepreload
557 del deepreload
558 except ImportError:
558 except ImportError:
559 pass
559 pass
560
560
561 # Save the current state of our namespace so that the interactive shell
561 # Save the current state of our namespace so that the interactive shell
562 # can later know which variables have been created by us from config files
562 # can later know which variables have been created by us from config files
563 # and loading. This way, loading a file (in any way) is treated just like
563 # and loading. This way, loading a file (in any way) is treated just like
564 # defining things on the command line, and %who works as expected.
564 # defining things on the command line, and %who works as expected.
565
565
566 # DON'T do anything that affects the namespace beyond this point!
566 # DON'T do anything that affects the namespace beyond this point!
567 IP.internal_ns.update(__main__.__dict__)
567 IP.internal_ns.update(__main__.__dict__)
568
568
569 #IP.internal_ns.update(locals()) # so our stuff doesn't show up in %who
569 #IP.internal_ns.update(locals()) # so our stuff doesn't show up in %who
570
570
571 # Now run through the different sections of the users's config
571 # Now run through the different sections of the users's config
572 if IP_rc.debug:
572 if IP_rc.debug:
573 print 'Trying to execute the following configuration structure:'
573 print 'Trying to execute the following configuration structure:'
574 print '(Things listed first are deeper in the inclusion tree and get'
574 print '(Things listed first are deeper in the inclusion tree and get'
575 print 'loaded first).\n'
575 print 'loaded first).\n'
576 pprint(IP_rc.__dict__)
576 pprint(IP_rc.__dict__)
577
577
578 for mod in IP_rc.import_mod:
578 for mod in IP_rc.import_mod:
579 try:
579 try:
580 exec 'import '+mod in IP.user_ns
580 exec 'import '+mod in IP.user_ns
581 except :
581 except :
582 IP.InteractiveTB()
582 IP.InteractiveTB()
583 import_fail_info(mod)
583 import_fail_info(mod)
584
584
585 for mod_fn in IP_rc.import_some:
585 for mod_fn in IP_rc.import_some:
586 if not mod_fn == []:
586 if not mod_fn == []:
587 mod,fn = mod_fn[0],','.join(mod_fn[1:])
587 mod,fn = mod_fn[0],','.join(mod_fn[1:])
588 try:
588 try:
589 exec 'from '+mod+' import '+fn in IP.user_ns
589 exec 'from '+mod+' import '+fn in IP.user_ns
590 except :
590 except :
591 IP.InteractiveTB()
591 IP.InteractiveTB()
592 import_fail_info(mod,fn)
592 import_fail_info(mod,fn)
593
593
594 for mod in IP_rc.import_all:
594 for mod in IP_rc.import_all:
595 try:
595 try:
596 exec 'from '+mod+' import *' in IP.user_ns
596 exec 'from '+mod+' import *' in IP.user_ns
597 except :
597 except :
598 IP.InteractiveTB()
598 IP.InteractiveTB()
599 import_fail_info(mod)
599 import_fail_info(mod)
600
600
601 for code in IP_rc.execute:
601 for code in IP_rc.execute:
602 try:
602 try:
603 exec code in IP.user_ns
603 exec code in IP.user_ns
604 except:
604 except:
605 IP.InteractiveTB()
605 IP.InteractiveTB()
606 warn('Failure executing code: ' + `code`)
606 warn('Failure executing code: ' + `code`)
607
607
608 # Execute the files the user wants in ipythonrc
608 # Execute the files the user wants in ipythonrc
609 for file in IP_rc.execfile:
609 for file in IP_rc.execfile:
610 try:
610 try:
611 file = filefind(file,sys.path+[IPython_dir])
611 file = filefind(file,sys.path+[IPython_dir])
612 except IOError:
612 except IOError:
613 warn(itpl('File $file not found. Skipping it.'))
613 warn(itpl('File $file not found. Skipping it.'))
614 else:
614 else:
615 IP.safe_execfile(os.path.expanduser(file),IP.user_ns)
615 IP.safe_execfile(os.path.expanduser(file),IP.user_ns)
616
616
617 # finally, try importing ipy_*_conf for final configuration
617 # finally, try importing ipy_*_conf for final configuration
618 try:
618 try:
619 import ipy_system_conf
619 import ipy_system_conf
620 except ImportError:
620 except ImportError:
621 if opts_all.debug: IP.InteractiveTB()
621 if opts_all.debug: IP.InteractiveTB()
622 warn("Could not import 'ipy_system_conf'")
622 warn("Could not import 'ipy_system_conf'")
623 except:
623 except:
624 IP.InteractiveTB()
624 IP.InteractiveTB()
625 import_fail_info('ipy_system_conf')
625 import_fail_info('ipy_system_conf')
626
626
627 # only import prof module if ipythonrc-PROF was not found
627 # only import prof module if ipythonrc-PROF was not found
628 if opts_all.profile and not profile_handled_by_legacy:
628 if opts_all.profile and not profile_handled_by_legacy:
629 profmodname = 'ipy_profile_' + opts_all.profile
629 profmodname = 'ipy_profile_' + opts_all.profile
630 try:
630 try:
631 force_import(profmodname)
631 force_import(profmodname)
632 except:
632 except:
633 IP.InteractiveTB()
633 IP.InteractiveTB()
634 print "Error importing",profmodname,\
634 print "Error importing",profmodname,\
635 "- perhaps you should run %upgrade?"
635 "- perhaps you should run %upgrade?"
636 import_fail_info(profmodname)
636 import_fail_info(profmodname)
637 else:
637 else:
638 opts.profile = opts_all.profile
638 opts.profile = opts_all.profile
639 else:
639 else:
640 force_import('ipy_profile_none')
640 force_import('ipy_profile_none')
641 # XXX - this is wrong: ipy_user_conf should not be loaded unconditionally,
641 # XXX - this is wrong: ipy_user_conf should not be loaded unconditionally,
642 # since the user could have specified a config file path by hand.
642 # since the user could have specified a config file path by hand.
643 try:
643 try:
644 force_import('ipy_user_conf')
644 force_import('ipy_user_conf')
645 except:
645 except:
646 conf = opts_all.ipythondir + "/ipy_user_conf.py"
646 conf = opts_all.ipythondir + "/ipy_user_conf.py"
647 IP.InteractiveTB()
647 IP.InteractiveTB()
648 if not os.path.isfile(conf):
648 if not os.path.isfile(conf):
649 warn(conf + ' does not exist, please run %upgrade!')
649 warn(conf + ' does not exist, please run %upgrade!')
650
650
651 import_fail_info("ipy_user_conf")
651 import_fail_info("ipy_user_conf")
652
652
653 # Define the history file for saving commands in between sessions
653 # Define the history file for saving commands in between sessions
654 try:
654 try:
655 histfname = 'history-%s' % opts.profile
655 histfname = 'history-%s' % opts.profile
656 except AttributeError:
656 except AttributeError:
657 histfname = 'history'
657 histfname = 'history'
658 IP.histfile = os.path.join(opts_all.ipythondir,histfname)
658 IP.histfile = os.path.join(opts_all.ipythondir,histfname)
659
659
660 # finally, push the argv to options again to ensure highest priority
660 # finally, push the argv to options again to ensure highest priority
661 IP_rc.update(opts)
661 IP_rc.update(opts)
662
662
663 # release stdout and stderr and save config log into a global summary
663 # release stdout and stderr and save config log into a global summary
664 msg.config.release_all()
664 msg.config.release_all()
665 if IP_rc.messages:
665 if IP_rc.messages:
666 msg.summary += msg.config.summary_all()
666 msg.summary += msg.config.summary_all()
667
667
668 #------------------------------------------------------------------------
668 #------------------------------------------------------------------------
669 # Setup interactive session
669 # Setup interactive session
670
670
671 # Now we should be fully configured. We can then execute files or load
671 # Now we should be fully configured. We can then execute files or load
672 # things only needed for interactive use. Then we'll open the shell.
672 # things only needed for interactive use. Then we'll open the shell.
673
673
674 # Take a snapshot of the user namespace before opening the shell. That way
674 # Take a snapshot of the user namespace before opening the shell. That way
675 # we'll be able to identify which things were interactively defined and
675 # we'll be able to identify which things were interactively defined and
676 # which were defined through config files.
676 # which were defined through config files.
677 IP.user_config_ns.update(IP.user_ns)
677 IP.user_config_ns.update(IP.user_ns)
678
678
679 # Force reading a file as if it were a session log. Slower but safer.
679 # Force reading a file as if it were a session log. Slower but safer.
680 if load_logplay:
680 if load_logplay:
681 print 'Replaying log...'
681 print 'Replaying log...'
682 try:
682 try:
683 if IP_rc.debug:
683 if IP_rc.debug:
684 logplay_quiet = 0
684 logplay_quiet = 0
685 else:
685 else:
686 logplay_quiet = 1
686 logplay_quiet = 1
687
687
688 msg.logplay.trap_all()
688 msg.logplay.trap_all()
689 IP.safe_execfile(load_logplay,IP.user_ns,
689 IP.safe_execfile(load_logplay,IP.user_ns,
690 islog = 1, quiet = logplay_quiet)
690 islog = 1, quiet = logplay_quiet)
691 msg.logplay.release_all()
691 msg.logplay.release_all()
692 if IP_rc.messages:
692 if IP_rc.messages:
693 msg.summary += msg.logplay.summary_all()
693 msg.summary += msg.logplay.summary_all()
694 except:
694 except:
695 warn('Problems replaying logfile %s.' % load_logplay)
695 warn('Problems replaying logfile %s.' % load_logplay)
696 IP.InteractiveTB()
696 IP.InteractiveTB()
697
697
698 # Load remaining files in command line
698 # Load remaining files in command line
699 msg.user_exec.trap_all()
699 msg.user_exec.trap_all()
700
700
701 # Do NOT execute files named in the command line as scripts to be loaded
701 # Do NOT execute files named in the command line as scripts to be loaded
702 # by embedded instances. Doing so has the potential for an infinite
702 # by embedded instances. Doing so has the potential for an infinite
703 # recursion if there are exceptions thrown in the process.
703 # recursion if there are exceptions thrown in the process.
704
704
705 # XXX FIXME: the execution of user files should be moved out to after
705 # XXX FIXME: the execution of user files should be moved out to after
706 # ipython is fully initialized, just as if they were run via %run at the
706 # ipython is fully initialized, just as if they were run via %run at the
707 # ipython prompt. This would also give them the benefit of ipython's
707 # ipython prompt. This would also give them the benefit of ipython's
708 # nice tracebacks.
708 # nice tracebacks.
709
709
710 if (not embedded and IP_rc.args and
710 if (not embedded and IP_rc.args and
711 not IP_rc.args[0].lower().endswith('.ipy')):
711 not IP_rc.args[0].lower().endswith('.ipy')):
712 name_save = IP.user_ns['__name__']
712 name_save = IP.user_ns['__name__']
713 IP.user_ns['__name__'] = '__main__'
713 IP.user_ns['__name__'] = '__main__'
714 # Set our own excepthook in case the user code tries to call it
714 # Set our own excepthook in case the user code tries to call it
715 # directly. This prevents triggering the IPython crash handler.
715 # directly. This prevents triggering the IPython crash handler.
716 old_excepthook,sys.excepthook = sys.excepthook, IP.excepthook
716 old_excepthook,sys.excepthook = sys.excepthook, IP.excepthook
717
717
718 save_argv = sys.argv[1:] # save it for later restoring
718 save_argv = sys.argv[1:] # save it for later restoring
719
719
720 sys.argv = args
720 sys.argv = args
721
721
722 try:
722 try:
723 IP.safe_execfile(args[0], IP.user_ns)
723 IP.safe_execfile(args[0], IP.user_ns)
724 finally:
724 finally:
725 # Reset our crash handler in place
725 # Reset our crash handler in place
726 sys.excepthook = old_excepthook
726 sys.excepthook = old_excepthook
727 sys.argv[:] = save_argv
727 sys.argv[:] = save_argv
728 IP.user_ns['__name__'] = name_save
728 IP.user_ns['__name__'] = name_save
729
729
730 msg.user_exec.release_all()
730 msg.user_exec.release_all()
731
731
732 if IP_rc.messages:
732 if IP_rc.messages:
733 msg.summary += msg.user_exec.summary_all()
733 msg.summary += msg.user_exec.summary_all()
734
734
735 # since we can't specify a null string on the cmd line, 0 is the equivalent:
735 # since we can't specify a null string on the cmd line, 0 is the equivalent:
736 if IP_rc.nosep:
736 if IP_rc.nosep:
737 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
737 IP_rc.separate_in = IP_rc.separate_out = IP_rc.separate_out2 = '0'
738 if IP_rc.separate_in == '0': IP_rc.separate_in = ''
738 if IP_rc.separate_in == '0': IP_rc.separate_in = ''
739 if IP_rc.separate_out == '0': IP_rc.separate_out = ''
739 if IP_rc.separate_out == '0': IP_rc.separate_out = ''
740 if IP_rc.separate_out2 == '0': IP_rc.separate_out2 = ''
740 if IP_rc.separate_out2 == '0': IP_rc.separate_out2 = ''
741 IP_rc.separate_in = IP_rc.separate_in.replace('\\n','\n')
741 IP_rc.separate_in = IP_rc.separate_in.replace('\\n','\n')
742 IP_rc.separate_out = IP_rc.separate_out.replace('\\n','\n')
742 IP_rc.separate_out = IP_rc.separate_out.replace('\\n','\n')
743 IP_rc.separate_out2 = IP_rc.separate_out2.replace('\\n','\n')
743 IP_rc.separate_out2 = IP_rc.separate_out2.replace('\\n','\n')
744
744
745 # Determine how many lines at the bottom of the screen are needed for
745 # Determine how many lines at the bottom of the screen are needed for
746 # showing prompts, so we can know wheter long strings are to be printed or
746 # showing prompts, so we can know wheter long strings are to be printed or
747 # paged:
747 # paged:
748 num_lines_bot = IP_rc.separate_in.count('\n')+1
748 num_lines_bot = IP_rc.separate_in.count('\n')+1
749 IP_rc.screen_length = IP_rc.screen_length - num_lines_bot
749 IP_rc.screen_length = IP_rc.screen_length - num_lines_bot
750
750
751 # configure startup banner
751 # configure startup banner
752 if IP_rc.c: # regular python doesn't print the banner with -c
752 if IP_rc.c: # regular python doesn't print the banner with -c
753 IP_rc.banner = 0
753 IP_rc.banner = 0
754 if IP_rc.banner:
754 if IP_rc.banner:
755 BANN_P = IP.BANNER_PARTS
755 BANN_P = IP.BANNER_PARTS
756 else:
756 else:
757 BANN_P = []
757 BANN_P = []
758
758
759 if IP_rc.profile: BANN_P.append('IPython profile: %s\n' % IP_rc.profile)
759 if IP_rc.profile: BANN_P.append('IPython profile: %s\n' % IP_rc.profile)
760
760
761 # add message log (possibly empty)
761 # add message log (possibly empty)
762 if msg.summary: BANN_P.append(msg.summary)
762 if msg.summary: BANN_P.append(msg.summary)
763 # Final banner is a string
763 # Final banner is a string
764 IP.BANNER = '\n'.join(BANN_P)
764 IP.BANNER = '\n'.join(BANN_P)
765
765
766 # Finalize the IPython instance. This assumes the rc structure is fully
766 # Finalize the IPython instance. This assumes the rc structure is fully
767 # in place.
767 # in place.
768 IP.post_config_initialization()
768 IP.post_config_initialization()
769
769
770 return IP
770 return IP
771 #************************ end of file <ipmaker.py> **************************
771 #************************ end of file <ipmaker.py> **************************
This diff has been collapsed as it changes many lines, (605 lines changed) Show them Hide them
@@ -1,417 +1,400 b''
1 # -*- coding: utf-8 -*-
1 #!/usr/bin/env python
2 """Mimic C structs with lots of extra functionality.
2 # encoding: utf-8
3 """A dict subclass that supports attribute style access.
4
5 Authors:
6
7 * Fernando Perez (original)
8 * Brian Granger (refactoring to a dict subclass)
3 """
9 """
4
10
5 #*****************************************************************************
11 #-----------------------------------------------------------------------------
6 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 # Copyright (C) 2008-2009 The IPython Development Team
7 #
13 #
8 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
10 #*****************************************************************************
16 #-----------------------------------------------------------------------------
11
17
12 __all__ = ['Struct']
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
13
21
14 import inspect
15 import types
16 import pprint
22 import pprint
17
23
18 from IPython.utils.genutils import list2dict2
24 from IPython.utils.genutils import list2dict2
19
25
20 class Struct(object):
26 __all__ = ['Struct']
21 """Class to mimic C structs but also provide convenient dictionary-like
27
22 functionality.
28 #-----------------------------------------------------------------------------
23
29 # Code
24 Instances can be initialized with a dictionary, a list of key=value pairs
30 #-----------------------------------------------------------------------------
25 or both. If both are present, the dictionary must come first.
31
26
32
27 Because Python classes provide direct assignment to their members, it's
33 class Struct(dict):
28 easy to overwrite normal methods (S.copy = 1 would destroy access to
34 """A dict subclass with attribute style access.
29 S.copy()). For this reason, all builtin method names are protected and
35
30 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
36 This dict subclass has a a few extra features:
31 a KeyError exception. If you really want to, you can bypass this
32 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
33 still work. Doing this will break functionality, though. As in most of
34 Python, namespace protection is weakly enforced, so feel free to shoot
35 yourself if you really want to.
36
37 Note that this class uses more memory and is *much* slower than a regular
38 dictionary, so be careful in situations where memory or performance are
39 critical. But for day to day use it should behave fine. It is particularly
40 convenient for storing configuration data in programs.
41
42 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
43 -/-= remove keys from the original. See the method descripitions.
44
45 This class allows a quick access syntax: both s.key and s['key'] are
46 valid. This syntax has a limitation: each 'key' has to be explicitly
47 accessed by its original name. The normal s.key syntax doesn't provide
48 access to the keys via variables whose values evaluate to the desired
49 keys. An example should clarify this:
50
51 Define a dictionary and initialize both with dict and k=v pairs:
52 >>> d={'a':1,'b':2}
53 >>> s=Struct(d,hi=10,ho=20)
54
55 The return of __repr__ can be used to create a new instance:
56 >>> s
57 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
58
59 Note: the special '__allownew' key is used for internal purposes.
60
61 __str__ (called by print) shows it's not quite a regular dictionary:
62 >>> print s
63 Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20})
64
65 Access by explicitly named key with dot notation:
66 >>> s.a
67 1
68
69 Or like a dictionary:
70 >>> s['a']
71 1
72
73 If you want a variable to hold the key value, only dictionary access works:
74 >>> key='hi'
75 >>> s.key
76 Traceback (most recent call last):
77 File "<stdin>", line 1, in ?
78 AttributeError: Struct instance has no attribute 'key'
79
80 >>> s[key]
81 10
82
83 Another limitation of the s.key syntax (and Struct(key=val)
84 initialization): keys can't be numbers. But numeric keys can be used and
85 accessed using the dictionary syntax. Again, an example:
86
87 This doesn't work (prompt changed to avoid confusing the test system):
88 ->> s=Struct(4='hi')
89 Traceback (most recent call last):
90 ...
91 SyntaxError: keyword can't be an expression
92
93 But this does:
94 >>> s=Struct()
95 >>> s[4]='hi'
96 >>> s
97 Struct({4: 'hi', '__allownew': True})
98 >>> s[4]
99 'hi'
100 """
101
37
102 # Attributes to which __setitem__ and __setattr__ will block access.
38 * Attribute style access.
103 # Note: much of this will be moot in Python 2.2 and will be done in a much
39 * Protection of class members (like keys, items) when using attribute
104 # cleaner way.
40 style access.
105 __protected = ('copy dict dictcopy get has_attr has_key items keys '
41 * The ability to restrict assignment to only existing keys.
106 'merge popitem setdefault update values '
42 * Intelligent merging.
107 '__make_dict __dict_invert ').split()
43 * Overloaded operators.
44 """
108
45
109 def __init__(self,data=None,**kw):
46 def __init__(self, *args, **kw):
110 """Initialize with a dictionary, another Struct, or data.
47 """Initialize with a dictionary, another Struct, or data.
111
48
112 Parameters
49 Parameters
113 ----------
50 ----------
114 data : dict, Struct
51 args : dict, Struct
115 Initialize with this data.
52 Initialize with one dict or Struct
116 kw : dict
53 kw : dict
117 Initialize with key, value pairs.
54 Initialize with key, value pairs.
118
55
119 Examples
56 Examples
120 --------
57 --------
121
58
59 >>> s = Struct(a=10,b=30)
60 >>> s.a
61 10
62 >>> s.b
63 30
64 >>> s2 = Struct(s,c=30)
65 >>> s2.keys()
66 ['a', 'c', 'b']
122 """
67 """
123 object.__setattr__(self, '_allownew', True)
68 object.__setattr__(self, '_allownew', True)
124 object.__setattr__(self, '_data',{})
69 dict.__init__(self, *args, **kw)
125 if data is None:
126 data = {}
127 if isinstance(data, Struct):
128 data = data.dict()
129 elif data and not isinstance(data, dict):
130 raise TypeError('initialize with a dict, Struct or key=val pairs')
131 data.update(kw)
132 # do the updating by hand to guarantee that we go through the
133 # safety-checked __setitem__
134 for k, v in data.items():
135 self[k] = v
136
70
137 def __setitem__(self, key, value):
71 def __setitem__(self, key, value):
138 """Used when struct[key] = val calls are made."""
72 """Set an item with check for allownew.
73
74 Examples
75 --------
76
77 >>> s = Struct()
78 >>> s['a'] = 10
79 >>> s.allow_new_attr(False)
80 >>> s['a'] = 10
81 >>> s['a']
82 10
83 >>> try:
84 ... s['b'] = 20
85 ... except KeyError:
86 ... print 'this is not allowed'
87 ...
88 this is not allowed
89 """
90 if not self._allownew and not self.has_key(key):
91 raise KeyError(
92 "can't create new attribute %s when allow_new_attr(False)" % key)
93 dict.__setitem__(self, key, value)
94
95 def __setattr__(self, key, value):
96 """Set an attr with protection of class members.
97
98 This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to
99 :exc:`AttributeError`.
100
101 Examples
102 --------
103
104 >>> s = Struct()
105 >>> s.a = 10
106 >>> s.a
107 10
108 >>> try:
109 ... s.get = 10
110 ... except AttributeError:
111 ... print "you can't set a class member"
112 ...
113 you can't set a class member
114 """
115 # If key is an str it might be a class member or instance var
139 if isinstance(key, str):
116 if isinstance(key, str):
140 # I can't simply call hasattr here because it calls getattr, which
117 # I can't simply call hasattr here because it calls getattr, which
141 # calls self.__getattr__, which returns True for keys in
118 # calls self.__getattr__, which returns True for keys in
142 # self._data. But I only want keys in the class and in
119 # self._data. But I only want keys in the class and in
143 # self.__dict__
120 # self.__dict__
144 if key in self.__dict__ or hasattr(Struct, key):
121 if key in self.__dict__ or hasattr(Struct, key):
145 raise KeyError(
122 raise AttributeError(
146 'key %s is a protected key of class Struct.' % key
123 'attr %s is a protected member of class Struct.' % key
147 )
124 )
148 if not self._allownew and key not in self._data:
125 try:
149 raise KeyError(
126 self.__setitem__(key, value)
150 "can't create unknown attribute %s. Check for typos, or use allow_new_attr" % key)
127 except KeyError, e:
151 self._data[key] = value
128 raise AttributeError(e)
152
153 def __setattr__(self, key, value):
154 self.__setitem__(key, value)
155
129
156 def __getattr__(self, key):
130 def __getattr__(self, key):
131 """Get an attr by calling :meth:`dict.__getitem__`.
132
133 Like :meth:`__setattr__`, this method converts :exc:`KeyError` to
134 :exc:`AttributeError`.
135
136 Examples
137 --------
138
139 >>> s = Struct(a=10)
140 >>> s.a
141 10
142 >>> type(s.get)
143 <type 'builtin_function_or_method'>
144 >>> try:
145 ... s.b
146 ... except AttributeError:
147 ... print "I don't have that key"
148 ...
149 I don't have that key
150 """
157 try:
151 try:
158 result = self._data[key]
152 result = self[key]
159 except KeyError:
153 except KeyError:
160 raise AttributeError(key)
154 raise AttributeError(key)
161 else:
155 else:
162 return result
156 return result
163
157
164 def __getitem__(self, key):
165 return self._data[key]
166
167 def __str__(self):
168 return 'Struct('+ pprint.pformat(self._data)+')'
169
170 def __repr__(self):
171 return self.__str__()
172
173 def __contains__(self, key):
174 return key in self._data
175
176 def __iadd__(self, other):
158 def __iadd__(self, other):
177 """S += S2 is a shorthand for S.merge(S2)."""
159 """s += s2 is a shorthand for s.merge(s2).
160
161 Examples
162 --------
163
164 >>> s = Struct(a=10,b=30)
165 >>> s2 = Struct(a=20,c=40)
166 >>> s += s2
167 >>> s
168 {'a': 10, 'c': 40, 'b': 30}
169 """
178 self.merge(other)
170 self.merge(other)
179 return self
171 return self
180
172
181 def __add__(self,other):
173 def __add__(self,other):
182 """S + S2 -> New Struct made from S.merge(S2)"""
174 """s + s2 -> New Struct made from s.merge(s2).
183 Sout = self.copy()
175
184 Sout.merge(other)
176 Examples
185 return Sout
177 --------
178
179 >>> s1 = Struct(a=10,b=30)
180 >>> s2 = Struct(a=20,c=40)
181 >>> s = s1 + s2
182 >>> s
183 {'a': 10, 'c': 40, 'b': 30}
184 """
185 sout = self.copy()
186 sout.merge(other)
187 return sout
186
188
187 def __sub__(self,other):
189 def __sub__(self,other):
188 """Out of place remove keys from self that are in other."""
190 """s1 - s2 -> remove keys in s2 from s1.
189 Sout = self.copy()
191
190 Sout -= other
192 Examples
191 return Sout
193 --------
194
195 >>> s1 = Struct(a=10,b=30)
196 >>> s2 = Struct(a=40)
197 >>> s = s1 - s2
198 >>> s
199 {'b': 30}
200 """
201 sout = self.copy()
202 sout -= other
203 return sout
192
204
193 def __isub__(self,other):
205 def __isub__(self,other):
194 """Inplace remove keys from self that are in other."""
206 """Inplace remove keys from self that are in other.
207
208 Examples
209 --------
210
211 >>> s1 = Struct(a=10,b=30)
212 >>> s2 = Struct(a=40)
213 >>> s1 -= s2
214 >>> s1
215 {'b': 30}
216 """
195 for k in other.keys():
217 for k in other.keys():
196 if self.has_key(k):
218 if self.has_key(k):
197 del self._data[k]
219 del self[k]
220 return self
198
221
199 def __make_dict(self,__loc_data__,**kw):
200 """Helper function for update and merge. Return a dict from data.
201 """
202 if __loc_data__ == None:
203 data = {}
204 elif isinstance(__loc_data__, dict):
205 data = __loc_data__
206 elif isinstance(__loc_data__, Struct):
207 data = __loc_data__._data
208 else:
209 raise TypeError('update with a dict, Struct or key=val pairs')
210 if kw:
211 data.update(kw)
212 return data
213
214 def __dict_invert(self, data):
222 def __dict_invert(self, data):
215 """Helper function for merge.
223 """Helper function for merge.
216
224
217 Takes a dictionary whose values are lists and returns a dict with
225 Takes a dictionary whose values are lists and returns a dict with
218 the elements of each list as keys and the original keys as values.
226 the elements of each list as keys and the original keys as values.
219 """
227 """
220 outdict = {}
228 outdict = {}
221 for k,lst in data.items():
229 for k,lst in data.items():
222 if isinstance(lst, str):
230 if isinstance(lst, str):
223 lst = lst.split()
231 lst = lst.split()
224 for entry in lst:
232 for entry in lst:
225 outdict[entry] = k
233 outdict[entry] = k
226 return outdict
234 return outdict
227
235
228 def clear(self):
229 """Clear all attributes."""
230 self._data.clear()
231
232 def copy(self):
233 """Return a (shallow) copy of a Struct."""
234 return Struct(self._data.copy())
235
236 def dict(self):
236 def dict(self):
237 """Return the Struct's dictionary."""
237 return self
238 return self._data
238
239
239 def copy(self):
240 def dictcopy(self):
240 """Return a copy as a Struct.
241 """Return a (shallow) copy of the Struct's dictionary."""
242 return self._data.copy()
243
244 def popitem(self):
245 """Return (key, value) tuple and remove from Struct.
246
241
247 If key is not present raise KeyError.
242 Examples
248 """
243 --------
249 return self._data.popitem()
250
251 def update(self,__loc_data__=None,**kw):
252 """Update (merge) with data from another Struct or dict.
253
244
254 Parameters
245 >>> s = Struct(a=10,b=30)
255 ----------
246 >>> s2 = s.copy()
256 __loc_data : dict, Struct
247 >>> s2
257 The new data to add to self.
248 {'a': 10, 'b': 30}
258 kw : dict
249 >>> type(s2).__name__
259 Key, value pairs to add to self.
250 'Struct'
251 """
252 return Struct(dict.copy(self))
253
254 def hasattr(self, key):
255 """hasattr function available as a method.
256
257 Implemented like has_key.
258
259 Examples
260 --------
261
262 >>> s = Struct(a=10)
263 >>> s.hasattr('a')
264 True
265 >>> s.hasattr('b')
266 False
267 >>> s.hasattr('get')
268 False
260 """
269 """
261 # The funny name __loc_data__ is to prevent a common variable name
270 return self.has_key(key)
262 # which could be a fieled of a Struct to collide with this
271
263 # parameter. The problem would arise if the function is called with a
272 def allow_new_attr(self, allow = True):
264 # keyword with this same name that a user means to add as a Struct
273 """Set whether new attributes can be created in this Struct.
265 # field.
274
266 newdict = self.__make_dict(__loc_data__, **kw)
275 This can be used to catch typos by verifying that the attribute user
267 for k, v in newdict.iteritems():
276 tries to change already exists in this Struct.
268 self[k] = v
277 """
269
278 object.__setattr__(self, '_allownew', allow)
279
270 def merge(self, __loc_data__=None, __conflict_solve=None, **kw):
280 def merge(self, __loc_data__=None, __conflict_solve=None, **kw):
271 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
281 """Merge two Structs with customizable conflict resolution.
272
282
273 This is similar to update(), but much more flexible. First, a dict is
283 This is similar to :meth:`update`, but much more flexible. First, a
274 made from data+key=value pairs. When merging this dict with the Struct
284 dict is made from data+key=value pairs. When merging this dict with
275 S, the optional dictionary 'conflict' is used to decide what to do.
285 the Struct S, the optional dictionary 'conflict' is used to decide
276
286 what to do.
287
277 If conflict is not given, the default behavior is to preserve any keys
288 If conflict is not given, the default behavior is to preserve any keys
278 with their current value (the opposite of the update method's
289 with their current value (the opposite of the :meth:`update` method's
279 behavior).
290 behavior).
280
291
281 conflict is a dictionary of binary functions which will be used to
292 Parameters
282 solve key conflicts. It must have the following structure:
293 ----------
283
294 __loc_data : dict, Struct
284 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
295 The data to merge into self
285
296 __conflict_solve : dict
286 Values must be lists or whitespace separated strings which are
297 The conflict policy dict. The keys are binary functions used to
287 automatically converted to lists of strings by calling string.split().
298 resolve the conflict and the values are lists of strings naming
288
299 the keys the conflict resolution function applies to. Instead of
289 Each key of conflict is a function which defines a policy for
300 a list of strings a space separated string can be used, like
290 resolving conflicts when merging with the input data. Each fn must be
301 'a b c'.
291 a binary function which returns the desired outcome for a key
302 kw : dict
292 conflict. These functions will be called as fn(old,new).
303 Additional key, value pairs to merge in
293
304
294 An example is probably in order. Suppose you are merging the struct S
305 Notes
295 with a dict D and the following conflict policy dict:
306 -----
296
307
297 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
308 The `__conflict_solve` dict is a dictionary of binary functions which will be used to
298
309 solve key conflicts. Here is an example::
299 If the key 'a' is found in both S and D, the merge method will call:
310
300
311 __conflict_solve = dict(
301 S['a'] = fn1(S['a'],D['a'])
312 func1=['a','b','c'],
302
313 func2=['d','e']
314 )
315
316 In this case, the function :func:`func1` will be used to resolve
317 keys 'a', 'b' and 'c' and the function :func:`func2` will be used for
318 keys 'd' and 'e'. This could also be written as::
319
320 __conflict_solve = dict(func1='a b c',func2='d e')
321
322 These functions will be called for each key they apply to with the
323 form::
324
325 func1(self['a'], other['a'])
326
327 The return value is used as the final merged value.
328
303 As a convenience, merge() provides five (the most commonly needed)
329 As a convenience, merge() provides five (the most commonly needed)
304 pre-defined policies: preserve, update, add, add_flip and add_s. The
330 pre-defined policies: preserve, update, add, add_flip and add_s. The
305 easiest explanation is their implementation:
331 easiest explanation is their implementation::
306
332
307 preserve = lambda old,new: old
333 preserve = lambda old,new: old
308 update = lambda old,new: new
334 update = lambda old,new: new
309 add = lambda old,new: old + new
335 add = lambda old,new: old + new
310 add_flip = lambda old,new: new + old # note change of order!
336 add_flip = lambda old,new: new + old # note change of order!
311 add_s = lambda old,new: old + ' ' + new # only works for strings!
337 add_s = lambda old,new: old + ' ' + new # only for str!
312
338
313 You can use those four words (as strings) as keys in conflict instead
339 You can use those four words (as strings) as keys instead
314 of defining them as functions, and the merge method will substitute
340 of defining them as functions, and the merge method will substitute
315 the appropriate functions for you. That is, the call
341 the appropriate functions for you.
316
342
317 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
318
319 will automatically substitute the functions preserve and add for the
320 names 'preserve' and 'add' before making any function calls.
321
322 For more complicated conflict resolution policies, you still need to
343 For more complicated conflict resolution policies, you still need to
323 construct your own functions. """
344 construct your own functions.
324
345
325 data_dict = self.__make_dict(__loc_data__,**kw)
346 Examples
326
347 --------
348
349 This show the default policy:
350
351 >>> s = Struct(a=10,b=30)
352 >>> s2 = Struct(a=20,c=40)
353 >>> s.merge(s2)
354 >>> s
355 {'a': 10, 'c': 40, 'b': 30}
356
357 Now, show how to specify a conflict dict:
358
359 >>> s = Struct(a=10,b=30)
360 >>> s2 = Struct(a=20,b=40)
361 >>> conflict = {'update':'a','add':'b'}
362 >>> s.merge(s2,conflict)
363 >>> s
364 {'a': 20, 'b': 70}
365 """
366
367 data_dict = dict(__loc_data__,**kw)
368
327 # policies for conflict resolution: two argument functions which return
369 # policies for conflict resolution: two argument functions which return
328 # the value that will go in the new struct
370 # the value that will go in the new struct
329 preserve = lambda old,new: old
371 preserve = lambda old,new: old
330 update = lambda old,new: new
372 update = lambda old,new: new
331 add = lambda old,new: old + new
373 add = lambda old,new: old + new
332 add_flip = lambda old,new: new + old # note change of order!
374 add_flip = lambda old,new: new + old # note change of order!
333 add_s = lambda old,new: old + ' ' + new
375 add_s = lambda old,new: old + ' ' + new
334
376
335 # default policy is to keep current keys when there's a conflict
377 # default policy is to keep current keys when there's a conflict
336 conflict_solve = list2dict2(self.keys(), default = preserve)
378 conflict_solve = list2dict2(self.keys(), default = preserve)
337
379
338 # the conflict_solve dictionary is given by the user 'inverted': we
380 # the conflict_solve dictionary is given by the user 'inverted': we
339 # need a name-function mapping, it comes as a function -> names
381 # need a name-function mapping, it comes as a function -> names
340 # dict. Make a local copy (b/c we'll make changes), replace user
382 # dict. Make a local copy (b/c we'll make changes), replace user
341 # strings for the three builtin policies and invert it.
383 # strings for the three builtin policies and invert it.
342 if __conflict_solve:
384 if __conflict_solve:
343 inv_conflict_solve_user = __conflict_solve.copy()
385 inv_conflict_solve_user = __conflict_solve.copy()
344 for name, func in [('preserve',preserve), ('update',update),
386 for name, func in [('preserve',preserve), ('update',update),
345 ('add',add), ('add_flip',add_flip),
387 ('add',add), ('add_flip',add_flip),
346 ('add_s',add_s)]:
388 ('add_s',add_s)]:
347 if name in inv_conflict_solve_user.keys():
389 if name in inv_conflict_solve_user.keys():
348 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
390 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
349 del inv_conflict_solve_user[name]
391 del inv_conflict_solve_user[name]
350 conflict_solve.update(self.__dict_invert(inv_conflict_solve_user))
392 conflict_solve.update(self.__dict_invert(inv_conflict_solve_user))
351 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
393 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
352 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
394 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
353 for key in data_dict:
395 for key in data_dict:
354 if key not in self:
396 if key not in self:
355 self[key] = data_dict[key]
397 self[key] = data_dict[key]
356 else:
398 else:
357 self[key] = conflict_solve[key](self[key],data_dict[key])
399 self[key] = conflict_solve[key](self[key],data_dict[key])
358
359 def has_key(self,key):
360 """Like has_key() dictionary method."""
361 return self._data.has_key(key)
362
363 def hasattr(self,key):
364 """hasattr function available as a method.
365
366 Implemented like has_key, to make sure that all available keys in the
367 internal dictionary of the Struct appear also as attributes (even
368 numeric keys)."""
369 return self._data.has_key(key)
370
371 def items(self):
372 """Return the items in the Struct's dictionary as (key, value)'s."""
373 return self._data.items()
374
375 def keys(self):
376 """Return the keys in the Struct's dictionary.."""
377 return self._data.keys()
378
379 def values(self, keys=None):
380 """Return the values in the Struct's dictionary.
381
382 Can be called with an optional argument keys, which must be a list or
383 tuple of keys. In this case it returns only the values corresponding
384 to those keys (allowing a form of 'slicing' for Structs).
385 """
386 if not keys:
387 return self._data.values()
388 else:
389 result=[]
390 for k in keys:
391 result.append(self[k])
392 return result
393
394 def get(self, attr, val=None):
395 """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None."""
396 try:
397 return self[attr]
398 except KeyError:
399 return val
400
401 def setdefault(self, attr, val=None):
402 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S"""
403 if not self._data.has_key(attr):
404 self[attr] = val
405 return self.get(attr, val)
406
407 def allow_new_attr(self, allow = True):
408 """Set whether new attributes can be created in this Struct.
409
410 This can be used to catch typos by verifying that the attribute user
411 tries to change already exists in this Struct.
412 """
413 object.__setattr__(self, '_allownew', allow)
414
415
416 # end class Struct
417
400
General Comments 0
You need to be logged in to leave comments. Login now