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